Merge m-c to autoland
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 19 Jan 2017 22:13:28 -0800
changeset 375256 24e2f44013cc68b2d8cca78ce6655ae39b121362
parent 375255 f1fef2537a70ff872a488a6ff09e0071520b8265 (current diff)
parent 375242 aa3e49299a3aa5cb0db570532e3df9e75d30c2d1 (diff)
child 375257 6dad16396f745d39086c813734fc1fd6323e9725
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to autoland
dom/u2f/NSSU2FTokenRemote.cpp
dom/u2f/NSSU2FTokenRemote.h
dom/u2f/ScopedCredential.cpp
dom/u2f/ScopedCredential.h
dom/u2f/ScopedCredentialInfo.cpp
dom/u2f/ScopedCredentialInfo.h
dom/u2f/USBToken.cpp
dom/u2f/USBToken.h
dom/u2f/WebAuthentication.cpp
dom/u2f/WebAuthentication.h
dom/u2f/WebAuthnAssertion.cpp
dom/u2f/WebAuthnAssertion.h
dom/u2f/WebAuthnAttestation.cpp
dom/u2f/WebAuthnAttestation.h
dom/u2f/WebAuthnRequest.h
dom/u2f/tests/test_webauthn_get_assertion.html
dom/u2f/tests/test_webauthn_loopback.html
dom/u2f/tests/test_webauthn_make_credential.html
dom/u2f/tests/test_webauthn_no_token.html
dom/u2f/tests/test_webauthn_sameorigin.html
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -88,17 +88,23 @@
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible: nsISupports and cycle collection
 
-NS_IMPL_CYCLE_COLLECTION(Accessible, mContent)
+NS_IMPL_CYCLE_COLLECTION_CLASS(Accessible)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Accessible)
+  tmp->Shutdown();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Accessible)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent, mDoc)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Accessible)
   if (aIID.Equals(NS_GET_IID(Accessible)))
     foundInterface = this;
   else
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, Accessible)
 NS_INTERFACE_MAP_END
 
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -1133,17 +1133,17 @@ protected:
 
   /**
    * Return group info.
    */
   AccGroupInfo* GetGroupInfo();
 
   // Data Members
   nsCOMPtr<nsIContent> mContent;
-  DocAccessible* mDoc;
+  RefPtr<DocAccessible> mDoc;
 
   Accessible* mParent;
   nsTArray<Accessible*> mChildren;
   int32_t mIndexInParent;
 
   static const uint8_t kStateFlagsBits = 13;
   static const uint8_t kContextFlagsBits = 3;
   static const uint8_t kTypeBits = 6;
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -75,28 +75,34 @@ static nsIAtom** kRelationAttrs[] =
 
 static const uint32_t kRelationAttrsLen = ArrayLength(kRelationAttrs);
 
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/desctructor
 
 DocAccessible::
   DocAccessible(nsIDocument* aDocument, nsIPresShell* aPresShell) :
-  HyperTextAccessibleWrap(nullptr, this),
+    // XXX don't pass a document to the Accessible constructor so that we don't
+    // set mDoc until our vtable is fully setup.  If we set mDoc before setting
+    // up the vtable we will call Accessible::AddRef() but not the overrides of
+    // it for subclasses.  It is important to call those overrides to avoid
+    // confusing leak checking machinary.
+  HyperTextAccessibleWrap(nullptr, nullptr),
   // XXX aaronl should we use an algorithm for the initial cache size?
   mAccessibleCache(kDefaultCacheLength),
   mNodeToAccessibleMap(kDefaultCacheLength),
   mDocumentNode(aDocument),
   mScrollPositionChangedTicks(0),
   mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
   mVirtualCursor(nullptr),
   mPresShell(aPresShell), mIPCDoc(nullptr)
 {
   mGenericTypes |= eDocument;
   mStateFlags |= eNotNodeMapEntry;
+  mDoc = this;
 
   MOZ_ASSERT(mPresShell, "should have been given a pres shell");
   mPresShell->SetDocAccessible(this);
 
   // If this is a XUL Document, it should not implement nsHyperText
   if (mDocumentNode && mDocumentNode->IsXULDocument())
     mGenericTypes &= ~eHyperText;
 }
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -85,20 +85,21 @@ AccessibleWrap::~AccessibleWrap()
 ITypeInfo* AccessibleWrap::gTypeInfo = nullptr;
 
 NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible)
 
 void
 AccessibleWrap::Shutdown()
 {
   if (mID != kNoID) {
-    auto doc = static_cast<DocAccessibleWrap*>(mDoc);
+    auto doc = static_cast<DocAccessibleWrap*>(mDoc.get());
     MOZ_ASSERT(doc);
     if (doc) {
       doc->RemoveID(mID);
+      mID = kNoID;
     }
   }
 
   Accessible::Shutdown();
 }
 
 //-----------------------------------------------------
 // IUnknown interface methods - see iunknown.h for documentation
--- a/build/clang-plugin/NoAddRefReleaseOnReturnChecker.cpp
+++ b/build/clang-plugin/NoAddRefReleaseOnReturnChecker.cpp
@@ -1,40 +1,32 @@
 /* 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/. */
 
 #include "NoAddRefReleaseOnReturnChecker.h"
 #include "CustomMatchers.h"
 
 void NoAddRefReleaseOnReturnChecker::registerMatchers(MatchFinder* AstMatcher) {
-  // First, look for direct parents of the MemberExpr.
-  AstMatcher->addMatcher(
-      callExpr(
-          callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
-          hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr()))
-                        .bind("member")))
-          .bind("node"),
-      this);
-  // Then, look for MemberExpr that need to be casted to the right type using
-  // an intermediary CastExpr before we get to the CallExpr.
-  AstMatcher->addMatcher(
-      callExpr(
-          callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
-          hasParent(castExpr(
-              hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr()))
-                            .bind("member")))))
-          .bind("node"),
-      this);
+  // Look for all of the calls to AddRef() or Release()
+  AstMatcher->addMatcher(memberExpr(isAddRefOrRelease(), hasParent(callExpr())).bind("member"),
+                         this);
 }
 
 void NoAddRefReleaseOnReturnChecker::check(
     const MatchFinder::MatchResult &Result) {
-  const Stmt *Node = Result.Nodes.getNodeAs<Stmt>("node");
-  const FunctionDecl *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
   const MemberExpr *Member = Result.Nodes.getNodeAs<MemberExpr>("member");
-  const CXXMethodDecl *Method =
-      dyn_cast<CXXMethodDecl>(Member->getMemberDecl());
+  const Expr *Base = IgnoreTrivials(Member->getBase());
 
-  diag(Node->getLocStart(),
-       "%1 cannot be called on the return value of %0",
-       DiagnosticIDs::Error) << Func << Method;
+  // Check if the call to AddRef() or Release() was made on the result of a call
+  // to a MOZ_NO_ADDREF_RELEASE_ON_RETURN function or method.
+  if (auto *Call = dyn_cast<CallExpr>(Base)) {
+    if (auto *Callee = Call->getDirectCallee()) {
+      if (hasCustomAnnotation(Callee, "moz_no_addref_release_on_return")) {
+        diag(Call->getLocStart(),
+             "%1 cannot be called on the return value of %0",
+             DiagnosticIDs::Error)
+          << Callee
+          << dyn_cast<CXXMethodDecl>(Member->getMemberDecl());
+      }
+    }
+  }
 }
--- a/build/clang-plugin/OverrideBaseCallChecker.cpp
+++ b/build/clang-plugin/OverrideBaseCallChecker.cpp
@@ -95,14 +95,18 @@ void OverrideBaseCallChecker::check(
     }
 
     // Loop through the body of our method and search for calls to
     // base methods
     evaluateExpression(Method->getBody(), MethodsList);
 
     // If list is not empty pop up errors
     for (auto BaseMethod : MethodsList) {
+      std::string QualName;
+      raw_string_ostream OS(QualName);
+      BaseMethod->printQualifiedName(OS);
+
       diag(Method->getLocation(), Error, DiagnosticIDs::Error)
-          << BaseMethod->getQualifiedNameAsString()
+          << OS.str()
           << Decl->getName();
     }
   }
 }
--- a/build/clang-plugin/SprintfLiteralChecker.cpp
+++ b/build/clang-plugin/SprintfLiteralChecker.cpp
@@ -60,14 +60,18 @@ void SprintfLiteralChecker::check(
   if (Type) {
     // Match calls like snprintf(x, 100, ...), where x is int[100];
     const IntegerLiteral *Literal = Result.Nodes.getNodeAs<IntegerLiteral>("immediate");
     if (!Literal) {
       // Match calls like: const int y = 100; snprintf(x, y, ...);
       Literal = Result.Nodes.getNodeAs<IntegerLiteral>("constant");
     }
 
-    if (Type->getSize().ule(Literal->getValue())) {
+    // We're going to assume here that the bitwidth of both of these values fits within 64 bits.
+    // and zero-extend both values to 64-bits before comparing them.
+    uint64_t Size = Type->getSize().getZExtValue();
+    uint64_t Lit = Literal->getValue().getZExtValue();
+    if (Size <= Lit) {
       diag(D->getLocStart(), Error, DiagnosticIDs::Error) << Name << Replacement;
       diag(D->getLocStart(), Note, DiagnosticIDs::Note) << Name;
     }
   }
 }
--- a/build/clang-plugin/Utils.h
+++ b/build/clang-plugin/Utils.h
@@ -301,17 +301,19 @@ inline bool typeIsRefPtr(QualType Q) {
   return false;
 }
 
 // The method defined in clang for ignoring implicit nodes doesn't work with
 // some AST trees. To get around this, we define our own implementation of
 // IgnoreTrivials.
 inline const Stmt *IgnoreTrivials(const Stmt *s) {
   while (true) {
-    if (auto *ewc = dyn_cast<ExprWithCleanups>(s)) {
+    if (!s) {
+      return nullptr;
+    } else if (auto *ewc = dyn_cast<ExprWithCleanups>(s)) {
       s = ewc->getSubExpr();
     } else if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s)) {
       s = mte->GetTemporaryExpr();
     } else if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s)) {
       s = bte->getSubExpr();
     } else if (auto *ice = dyn_cast<ImplicitCastExpr>(s)) {
       s = ice->getSubExpr();
     } else if (auto *pe = dyn_cast<ParenExpr>(s)) {
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -391,16 +391,19 @@ def split_triplet(triplet):
         canonical_cpu = 'mips32'
         endianness = 'little' if 'el' in cpu else 'big'
     elif cpu in ('mips64', 'mips64el'):
         canonical_cpu = 'mips64'
         endianness = 'little' if 'el' in cpu else 'big'
     elif cpu.startswith('aarch64'):
         canonical_cpu = 'aarch64'
         endianness = 'little'
+    elif cpu == 'sh4':
+        canonical_cpu = 'sh4'
+        endianness = 'little'
     else:
         die('Unknown CPU type: %s' % cpu)
 
     return namespace(
         alias=triplet,
         cpu=CPU(canonical_cpu),
         bitness=CPU_bitness[canonical_cpu],
         kernel=Kernel(canonical_kernel),
new file mode 100644
--- /dev/null
+++ b/docshell/base/PendingGlobalHistoryEntry.cpp
@@ -0,0 +1,75 @@
+#include "mozilla/dom/PendingGlobalHistoryEntry.h"
+
+namespace mozilla {
+
+namespace dom {
+
+void
+PendingGlobalHistoryEntry::VisitURI(nsIURI* aURI,
+                             nsIURI* aLastVisitedURI,
+                             nsIURI* aReferrerURI,
+                             uint32_t aFlags)
+{
+  URIVisit visit;
+  visit.mURI = aURI;
+  visit.mLastVisitedURI = aLastVisitedURI;
+  visit.mReferrerURI = aReferrerURI;
+  visit.mFlags = aFlags;
+  mVisits.AppendElement(Move(visit));
+}
+
+void
+PendingGlobalHistoryEntry::SetURITitle(nsIURI* aURI,
+                                const nsAString& aTitle)
+{
+  URITitle title;
+  title.mURI = aURI;
+  title.mTitle.Assign(aTitle);
+  mTitles.AppendElement(title);
+}
+
+nsresult
+PendingGlobalHistoryEntry::ApplyChanges(IHistory* aHistory)
+{
+  nsresult rv;
+  for (const URIVisit& visit : mVisits) {
+    rv = aHistory->VisitURI(visit.mURI, visit.mLastVisitedURI, visit.mFlags);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  mVisits.Clear();
+
+  for (const URITitle& title : mTitles) {
+    aHistory->SetURITitle(title.mURI, title.mTitle);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  mTitles.Clear();
+
+  return NS_OK;
+}
+
+nsresult
+PendingGlobalHistoryEntry::ApplyChanges(nsIGlobalHistory2* aHistory)
+{
+  nsresult rv;
+  for (const URIVisit& visit : mVisits) {
+    bool redirect = (visit.mFlags & IHistory::REDIRECT_TEMPORARY) ||
+      (visit.mFlags & IHistory::REDIRECT_PERMANENT);
+    bool toplevel = (visit.mFlags & IHistory::TOP_LEVEL);
+
+    rv = aHistory->AddURI(visit.mURI, redirect, toplevel, visit.mReferrerURI);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  mVisits.Clear();
+
+  for (const URITitle& title : mTitles) {
+    rv = aHistory->SetPageTitle(title.mURI, title.mTitle);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  mTitles.Clear();
+
+  return NS_OK;
+}
+
+} // namespace dom
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/docshell/base/PendingGlobalHistoryEntry.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef mozilla_dom_PendingGlobalHistoryEntry_h_
+#define mozilla_dom_PendingGlobalHistoryEntry_h_
+
+#include "mozilla/IHistory.h"
+#include "nsIGlobalHistory2.h"
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+namespace dom {
+
+// This class acts as a wrapper around a IHistory, in that it can be used to
+// record a series of operations on the IHistory, and then play them back at a
+// later time. This is used in Prerendering in order to record the Global
+// History changes being made by the prerendering docshell and then play them
+// back when the docshell is finished rendering.
+//
+// This class also handles applying the changes to nsIGlobalHistory2.
+class PendingGlobalHistoryEntry
+{
+public:
+  void VisitURI(nsIURI* aURI,
+                nsIURI* aLastVisitedURI,
+                nsIURI* aReferrerURI,
+                uint32_t aFlags);
+
+  void SetURITitle(nsIURI* aURI,
+                   const nsAString& aTitle);
+
+  nsresult ApplyChanges(IHistory* aHistory);
+
+  nsresult ApplyChanges(nsIGlobalHistory2* aHistory);
+
+private:
+  struct URIVisit
+  {
+    nsCOMPtr<nsIURI> mURI;
+    nsCOMPtr<nsIURI> mLastVisitedURI;
+    nsCOMPtr<nsIURI> mReferrerURI;
+    uint32_t mFlags = 0;
+  };
+  struct URITitle
+  {
+    nsCOMPtr<nsIURI> mURI;
+    nsString mTitle;
+  };
+  nsTArray<URIVisit> mVisits;
+  nsTArray<URITitle> mTitles;
+};
+
+} // namespace dom
+
+} // namespace mozilla
+
+#endif // mozilla_dom_PendingGlobalHistoryEntry_h_
+
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -51,30 +51,35 @@ EXPORTS += [
     'SerializedLoadContext.h',
 ]
 
 EXPORTS.mozilla += [
     'IHistory.h',
     'LoadContext.h',
 ]
 
+EXPORTS.mozilla.dom += [
+    'PendingGlobalHistoryEntry.h',
+]
+
 UNIFIED_SOURCES += [
     'LoadContext.cpp',
     'nsAboutRedirector.cpp',
     'nsContextMenuInfo.cpp',
     'nsDefaultURIFixup.cpp',
     'nsDocShell.cpp',
     'nsDocShellEditorData.cpp',
     'nsDocShellEnumerator.cpp',
     'nsDocShellLoadInfo.cpp',
     'nsDocShellTransferableHooks.cpp',
     'nsDocShellTreeOwner.cpp',
     'nsDownloadHistory.cpp',
     'nsDSURIContentListener.cpp',
     'nsWebNavigationInfo.cpp',
+    'PendingGlobalHistoryEntry.cpp',
     'SerializedLoadContext.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/docshell/shistory',
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/Casting.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ChromeUtils.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/PendingGlobalHistoryEntry.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/LoadInfo.h"
@@ -6166,17 +6167,29 @@ nsDocShell::SetIsActive(bool aIsActive)
   if (mItemType == nsIDocShellTreeItem::typeChrome) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // Keep track ourselves.
   mIsActive = aIsActive;
 
   // Clear prerender flag if necessary.
-  mIsPrerendered &= !aIsActive;
+  if (mIsPrerendered && aIsActive) {
+    MOZ_ASSERT(mPrerenderGlobalHistory.get());
+    mIsPrerendered = false;
+    nsCOMPtr<IHistory> history = services::GetHistoryService();
+    nsresult rv = NS_OK;
+    if (history) {
+      rv = mPrerenderGlobalHistory->ApplyChanges(history);
+    } else if (mGlobalHistory) {
+      rv = mPrerenderGlobalHistory->ApplyChanges(mGlobalHistory);
+    }
+    mPrerenderGlobalHistory = nullptr;
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   // Tell the PresShell about it.
   nsCOMPtr<nsIPresShell> pshell = GetPresShell();
   if (pshell) {
     pshell->SetIsActive(aIsActive);
   }
 
   // Tell the window about it
@@ -6247,16 +6260,17 @@ nsDocShell::GetIsActive(bool* aIsActive)
 
 NS_IMETHODIMP
 nsDocShell::SetIsPrerendered()
 {
   MOZ_ASSERT(!mIsPrerendered,
              "SetIsPrerendered() called on already prerendered docshell");
   SetIsActive(false);
   mIsPrerendered = true;
+  mPrerenderGlobalHistory = mozilla::MakeUnique<PendingGlobalHistoryEntry>();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetIsPrerendered(bool* aIsPrerendered)
 {
   *aIsPrerendered = mIsPrerendered;
   return NS_OK;
@@ -6497,24 +6511,18 @@ nsDocShell::SetTitle(const char16_t* aTi
   if (!parent) {
     nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
     if (treeOwnerAsWin) {
       treeOwnerAsWin->SetTitle(aTitle);
     }
   }
 
   AssertOriginAttributesMatchPrivateBrowsing();
-  if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE && mUseGlobalHistory &&
-      !UsePrivateBrowsing()) {
-    nsCOMPtr<IHistory> history = services::GetHistoryService();
-    if (history) {
-      history->SetURITitle(mCurrentURI, mTitle);
-    } else if (mGlobalHistory) {
-      mGlobalHistory->SetPageTitle(mCurrentURI, nsString(mTitle));
-    }
+  if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
+    UpdateGlobalHistoryTitle(mCurrentURI);
   }
 
   // Update SessionHistory with the document's title.
   if (mOSHE && mLoadType != LOAD_BYPASS_HISTORY &&
       mLoadType != LOAD_ERROR_PAGE) {
     mOSHE->SetTitle(mTitle);
   }
 
@@ -10395,24 +10403,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
         nsCOMPtr<nsISHEntry> shEntry;
         mSessionHistory->GetEntryAtIndex(index, false, getter_AddRefs(shEntry));
         NS_ENSURE_TRUE(shEntry, NS_ERROR_FAILURE);
         shEntry->SetTitle(mTitle);
       }
 
       /* Set the title for the Global History entry for this anchor url.
        */
-      if (mUseGlobalHistory && !UsePrivateBrowsing()) {
-        nsCOMPtr<IHistory> history = services::GetHistoryService();
-        if (history) {
-          history->SetURITitle(aURI, mTitle);
-        } else if (mGlobalHistory) {
-          mGlobalHistory->SetPageTitle(aURI, mTitle);
-        }
-      }
+      UpdateGlobalHistoryTitle(aURI);
 
       SetDocCurrentStateObj(mOSHE);
 
       // Inform the favicon service that the favicon for oldURI also
       // applies to aURI.
       CopyFavicon(currentURI, aURI, doc->NodePrincipal(), UsePrivateBrowsing());
 
       RefPtr<nsGlobalWindow> scriptGlobal = mScriptGlobal;
@@ -12088,24 +12089,17 @@ nsDocShell::AddState(JS::Handle<JS::Valu
     if (mLoadType != LOAD_ERROR_PAGE) {
       FireDummyOnLocationChange();
     }
 
     AddURIVisit(newURI, oldURI, oldURI, 0);
 
     // AddURIVisit doesn't set the title for the new URI in global history,
     // so do that here.
-    if (mUseGlobalHistory && !UsePrivateBrowsing()) {
-      nsCOMPtr<IHistory> history = services::GetHistoryService();
-      if (history) {
-        history->SetURITitle(newURI, mTitle);
-      } else if (mGlobalHistory) {
-        mGlobalHistory->SetPageTitle(newURI, mTitle);
-      }
-    }
+    UpdateGlobalHistoryTitle(newURI);
 
     // Inform the favicon service that our old favicon applies to this new
     // URI.
     CopyFavicon(oldURI, newURI, document->NodePrincipal(), UsePrivateBrowsing());
   } else {
     FireDummyOnLocationChange();
   }
   document->SetStateObject(scContainer);
@@ -13047,43 +13041,54 @@ nsDocShell::AddURIVisit(nsIURI* aURI,
   // Only content-type docshells save URI visits.  Also don't do
   // anything here if we're not supposed to use global history.
   if (mItemType != typeContent || !mUseGlobalHistory || UsePrivateBrowsing()) {
     return;
   }
 
   nsCOMPtr<IHistory> history = services::GetHistoryService();
 
-  if (history) {
+  if (mPrerenderGlobalHistory || history) {
     uint32_t visitURIFlags = 0;
 
     if (!IsFrame()) {
       visitURIFlags |= IHistory::TOP_LEVEL;
     }
 
     if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
       visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
     } else if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) {
       visitURIFlags |= IHistory::REDIRECT_PERMANENT;
+    } else {
+      MOZ_ASSERT(!aChannelRedirectFlags,
+                 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
+                 "if any flags in aChannelRedirectFlags is set.");
     }
 
     if (aResponseStatus >= 300 && aResponseStatus < 400) {
       visitURIFlags |= IHistory::REDIRECT_SOURCE;
     }
     // Errors 400-501 and 505 are considered unrecoverable, in the sense a
     // simple retry attempt by the user is unlikely to solve them.
     // 408 is special cased, since may actually indicate a temporary
     // connection problem.
     else if (aResponseStatus != 408 &&
              ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
               aResponseStatus == 505)) {
       visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
     }
 
-    (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
+    if (mPrerenderGlobalHistory) {
+      mPrerenderGlobalHistory->VisitURI(aURI,
+                                        aPreviousURI,
+                                        aReferrerURI,
+                                        visitURIFlags);
+    } else {
+      (void)history->VisitURI(aURI, aPreviousURI, visitURIFlags);
+    }
   } else if (mGlobalHistory) {
     // Falls back to sync global history interface.
     (void)mGlobalHistory->AddURI(aURI,
                                  !!aChannelRedirectFlags,
                                  !IsFrame(),
                                  aReferrerURI);
   }
 }
@@ -14460,16 +14465,31 @@ nsDocShell::HasUnloadedParent()
     if (inUnload) {
       return true;
     }
     parent = parent->GetParentDocshell();
   }
   return false;
 }
 
+void
+nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI)
+{
+  if (mUseGlobalHistory && !UsePrivateBrowsing()) {
+    nsCOMPtr<IHistory> history = services::GetHistoryService();
+    if (mPrerenderGlobalHistory) {
+      mPrerenderGlobalHistory->SetURITitle(aURI, mTitle);
+    } else if (history) {
+      history->SetURITitle(aURI, mTitle);
+    } else if (mGlobalHistory) {
+      mGlobalHistory->SetPageTitle(aURI, nsString(mTitle));
+    }
+  }
+}
+
 bool
 nsDocShell::IsInvisible()
 {
   return mInvisible;
 }
 
 void
 nsDocShell::SetInvisible(bool aInvisible)
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -59,16 +59,17 @@
 #include "prtime.h"
 #include "nsRect.h"
 #include "Units.h"
 #include "nsIDeprecationWarner.h"
 
 namespace mozilla {
 namespace dom {
 class EventTarget;
+class PendingGlobalHistoryEntry;
 typedef uint32_t ScreenOrientationInternal;
 } // namespace dom
 } // namespace mozilla
 
 class nsDocShell;
 class nsDOMNavigationTiming;
 class nsGlobalWindow;
 class nsIController;
@@ -802,16 +803,18 @@ protected:
         return EmptyCString();
     }
   }
 
   uint32_t GetInheritedFrameType();
 
   bool HasUnloadedParent();
 
+  void UpdateGlobalHistoryTitle(nsIURI* aURI);
+
   // Dimensions of the docshell
   nsIntRect mBounds;
   nsString mName;
   nsString mTitle;
   nsString mCustomUserAgent;
 
   /**
    * Content-Type Hint of the most-recently initiated load. Used for
@@ -1038,16 +1041,18 @@ private:
   nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
   nsTObserverArray<nsWeakPtr> mPrivacyObservers;
   nsTObserverArray<nsWeakPtr> mReflowObservers;
   nsTObserverArray<nsWeakPtr> mScrollObservers;
   nsCString mOriginalUriString;
   nsWeakPtr mOpener;
   mozilla::OriginAttributes mOriginAttributes;
 
+  mozilla::UniquePtr<mozilla::dom::PendingGlobalHistoryEntry> mPrerenderGlobalHistory;
+
   // A depth count of how many times NotifyRunToCompletionStart
   // has been called without a matching NotifyRunToCompletionStop.
   uint32_t mJSRunToCompletionDepth;
 
   // Whether or not touch events are overridden. Possible values are defined
   // as constants in the nsIDocShell.idl file.
   uint32_t mTouchEventsOverride;
 
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -23,17 +23,24 @@ namespace dom {
 static StaticRefPtr<TabGroup> sChromeTabGroup;
 
 TabGroup::TabGroup(bool aIsChrome)
  : mLastWindowLeft(false)
  , mThrottledQueuesInitialized(false)
 {
   for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
     TaskCategory category = static_cast<TaskCategory>(i);
-    mEventTargets[i] = CreateEventTargetFor(category);
+    if (aIsChrome) {
+      // The chrome TabGroup dispatches directly to the main thread. This means
+      // that we don't have to worry about cyclical references when cleaning up
+      // the chrome TabGroup.
+      mEventTargets[i] = do_GetMainThread();
+    } else {
+      mEventTargets[i] = CreateEventTargetFor(category);
+    }
   }
 
   // Do not throttle runnables from chrome windows.  In theory we should
   // not have abuse issues from these windows and many browser chrome
   // tests have races that fail if we do throttle chrome runnables.
   if (aIsChrome) {
     MOZ_ASSERT(!sChromeTabGroup);
     return;
@@ -71,17 +78,17 @@ TabGroup::EnsureThrottledEventQueues()
         // This may return nullptr during xpcom shutdown.  This is ok as we
         // do not guarantee a ThrottledEventQueue will be present.
         mEventTargets[i] = target;
       }
     }
   }
 }
 
-TabGroup*
+/* static */ TabGroup*
 TabGroup::GetChromeTabGroup()
 {
   if (!sChromeTabGroup) {
     sChromeTabGroup = new TabGroup(true /* chrome tab group */);
     ClearOnShutdown(&sChromeTabGroup);
   }
   return sChromeTabGroup;
 }
@@ -157,17 +164,20 @@ TabGroup::Join(nsPIDOMWindowOuter* aWind
 }
 
 void
 TabGroup::Leave(nsPIDOMWindowOuter* aWindow)
 {
   MOZ_ASSERT(mWindows.Contains(aWindow));
   mWindows.RemoveElement(aWindow);
 
-  if (mWindows.IsEmpty()) {
+  // The Chrome TabGroup doesn't have cyclical references through mEventTargets
+  // to itself, meaning that we don't have to worry about nulling mEventTargets
+  // out after the last window leaves.
+  if (sChromeTabGroup != this && mWindows.IsEmpty()) {
     mLastWindowLeft = true;
 
     // There is a RefPtr cycle TabGroup -> DispatcherEventTarget -> TabGroup. To
     // avoid leaks, we need to break the chain somewhere. We shouldn't be using
     // the ThrottledEventQueue for this TabGroup when no windows belong to it,
     // so it's safe to null out the queue here.
     for (size_t i = 0; i < size_t(TaskCategory::Count); i++) {
       mEventTargets[i] = nullptr;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9694,70 +9694,44 @@ nsContentUtils::AttemptLargeAllocationLo
     return false;
   }
 
   nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(window);
   if (NS_WARN_IF(!outer)) {
     return false;
   }
 
-  nsIDocument* doc = outer->GetExtantDoc();
-
   if (!XRE_IsContentProcess()) {
-    if (doc) {
-      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-                                      NS_LITERAL_CSTRING("DOM"),
-                                      doc,
-                                      nsContentUtils::eDOM_PROPERTIES,
-                                      "LargeAllocationNonE10S");
-    }
+    outer->SetLargeAllocStatus(LargeAllocStatus::NON_E10S);
     return false;
   }
 
   nsIDocShell* docShell = outer->GetDocShell();
   if (!docShell->GetIsOnlyToplevelInTabGroup()) {
-    if (doc) {
-      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-                                      NS_LITERAL_CSTRING("DOM"),
-                                      doc,
-                                      nsContentUtils::eDOM_PROPERTIES,
-                                      "LargeAllocationNotOnlyToplevelInTabGroup");
-    }
+    outer->SetLargeAllocStatus(LargeAllocStatus::NOT_ONLY_TOPLEVEL_IN_TABGROUP);
     return false;
   }
 
   // Get the request method, and check if it is a GET request. If it is not GET,
   // then we cannot perform a large allocation load.
   nsAutoCString requestMethod;
   rv = aChannel->GetRequestMethod(requestMethod);
   NS_ENSURE_SUCCESS(rv, false);
 
   if (NS_WARN_IF(!requestMethod.LowerCaseEqualsLiteral("get"))) {
-    if (doc) {
-      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-                                      NS_LITERAL_CSTRING("DOM"),
-                                      doc,
-                                      nsContentUtils::eDOM_PROPERTIES,
-                                      "LargeAllocationNonGetRequest");
-    }
+    outer->SetLargeAllocStatus(LargeAllocStatus::NON_GET);
     return false;
   }
 
-  TabChild* tabChild = TabChild::GetFrom(outer);
+  TabChild* tabChild = TabChild::GetFrom(outer->AsOuter());
   NS_ENSURE_TRUE(tabChild, false);
 
   if (tabChild->TakeIsFreshProcess())  {
     NS_WARNING("Already in a fresh process, ignoring Large-Allocation header!");
-    if (doc) {
-      nsContentUtils::ReportToConsole(nsIScriptError::infoFlag,
-                                      NS_LITERAL_CSTRING("DOM"),
-                                      doc,
-                                      nsContentUtils::eDOM_PROPERTIES,
-                                      "LargeAllocationSuccess");
-    }
+    outer->SetLargeAllocStatus(LargeAllocStatus::SUCCESS);
     return false;
   }
 
   // At this point the fress process load should succeed! We just need to get
   // ourselves a nsIWebBrowserChrome3 to ask to perform the reload. We should
   // have one, as we have already confirmed that we are running in a content
   // process.
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -650,17 +650,18 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMW
   mIsActive(false), mIsBackground(false),
   mMediaSuspend(Preferences::GetBool("media.block-autoplay-until-in-foreground", true) ?
     nsISuspendedTypes::SUSPENDED_BLOCK : nsISuspendedTypes::NONE_SUSPENDED),
   mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
   mDesktopModeViewport(false), mIsRootOuterWindow(false), mInnerWindow(nullptr),
   mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
-  mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false)
+  mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false),
+  mLargeAllocStatus(LargeAllocStatus::NONE)
 {
   if (aOuterWindow) {
     mTimeoutManager =
       MakeUnique<mozilla::dom::TimeoutManager>(*nsGlobalWindow::Cast(AsInner()));
   }
 }
 
 template<class T>
@@ -2929,16 +2930,21 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       newInnerWindow->mHasNotifiedGlobalCreated = true;
       nsContentUtils::AddScriptRunner(
         NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated));
     }
   }
 
   PreloadLocalStorage();
 
+  // If we have a recorded interesting Large-Allocation header status, report it
+  // to the newly attached document.
+  ReportLargeAllocStatus();
+  mLargeAllocStatus = LargeAllocStatus::NONE;
+
   return NS_OK;
 }
 
 void
 nsGlobalWindow::PreloadLocalStorage()
 {
   MOZ_ASSERT(IsOuterWindow());
 
@@ -13899,16 +13905,58 @@ nsGlobalWindow::CreateNamedPropertiesObj
 
 bool
 nsGlobalWindow::GetIsPrerendered()
 {
   nsIDocShell* docShell = GetDocShell();
   return docShell && docShell->GetIsPrerendered();
 }
 
+void
+nsPIDOMWindowOuter::SetLargeAllocStatus(LargeAllocStatus aStatus)
+{
+  MOZ_ASSERT(mLargeAllocStatus == LargeAllocStatus::NONE);
+  mLargeAllocStatus = aStatus;
+}
+
+void
+nsGlobalWindow::ReportLargeAllocStatus()
+{
+  MOZ_RELEASE_ASSERT(IsOuterWindow());
+
+  uint32_t errorFlags = nsIScriptError::warningFlag;
+  const char* message = nullptr;
+
+  switch (mLargeAllocStatus) {
+    case LargeAllocStatus::SUCCESS:
+      // Override the error flags such that the success message isn't reported
+      // as a warning.
+      errorFlags = nsIScriptError::infoFlag;
+      message = "LargeAllocationSuccess";
+      break;
+    case LargeAllocStatus::NON_GET:
+      message = "LargeAllocationNonGetRequest";
+      break;
+    case LargeAllocStatus::NON_E10S:
+      message = "LargeAllocationNonE10S";
+      break;
+    case LargeAllocStatus::NOT_ONLY_TOPLEVEL_IN_TABGROUP:
+      message = "LargeAllocationNotOnlyToplevelInTabGroup";
+      break;
+    default: // LargeAllocStatus::NONE
+      return; // Don't report a message to the console
+  }
+
+  nsContentUtils::ReportToConsole(errorFlags,
+                                  NS_LITERAL_CSTRING("DOM"),
+                                  mDoc,
+                                  nsContentUtils::eDOM_PROPERTIES,
+                                  message);
+}
+
 #ifdef MOZ_B2G
 void
 nsGlobalWindow::EnableNetworkEvent(EventMessage aEventMessage)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIPermissionManager> permMgr =
     services::GetPermissionManager();
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1557,16 +1557,20 @@ public:
   PopupControlState RevisePopupAbuseLevel(PopupControlState);
   void     FireAbuseEvents(const nsAString &aPopupURL,
                            const nsAString &aPopupWindowName,
                            const nsAString &aPopupWindowFeatures);
   void FireOfflineStatusEventIfChanged();
 
   bool GetIsPrerendered();
 
+private:
+  void ReportLargeAllocStatus();
+
+public:
   // Inner windows only.
   nsresult ScheduleNextIdleObserverCallback();
   uint32_t GetFuzzTimeMS();
   nsresult ScheduleActiveTimerCallback();
   uint32_t FindInsertionIndex(IdleObserverHolder* aIdleObserver);
   virtual nsresult RegisterIdleObserver(nsIIdleObserver* aIdleObserverPtr) override;
   nsresult FindIndexOfElementToRemove(nsIIdleObserver* aIdleObserver,
                                       int32_t* aRemoveElementIndex);
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -83,16 +83,39 @@ enum class FullscreenReason
   // Fullscreen API is the API provided to untrusted content.
   ForFullscreenAPI,
   // This reason can only be used with exiting fullscreen.
   // It is otherwise identical to eForFullscreenAPI except it would
   // suppress the fullscreen transition.
   ForForceExitFullscreen
 };
 
+namespace mozilla {
+namespace dom {
+// The states in this enum represent the different possible outcomes which the
+// window could be experiencing of loading a document with the
+// Large-Allocation header. The NONE case represents the case where no
+// Large-Allocation header was set.
+enum class LargeAllocStatus : uint8_t
+{
+  // These are the OK states, NONE means that no large allocation status message
+  // should be printed, while SUCCESS means that the success message should be
+  // printed.
+  NONE,
+  SUCCESS,
+
+  // These are the ERROR states. If a window is in one of these states, then the
+  // next document loaded in that window should have an error message reported
+  // to it.
+  NON_GET,
+  NON_E10S,
+  NOT_ONLY_TOPLEVEL_IN_TABGROUP
+};
+} // namespace dom
+} // namespace mozilla
 
 // nsPIDOMWindowInner and nsPIDOMWindowOuter are identical in all respects
 // except for the type name. They *must* remain identical so that we can
 // reinterpret_cast between them.
 template<class T>
 class nsPIDOMWindow : public T
 {
 public:
@@ -705,16 +728,18 @@ protected:
   // the (chrome|content)-document-global-created notification.
   bool mHasNotifiedGlobalCreated;
 
   uint32_t mMarkedCCGeneration;
 
   // Let the service workers plumbing know that some feature are enabled while
   // testing.
   bool mServiceWorkersTestingEnabled;
+
+  mozilla::dom::LargeAllocStatus mLargeAllocStatus; // Outer window only
 };
 
 #define NS_PIDOMWINDOWINNER_IID \
 { 0x775dabc9, 0x8f43, 0x4277, \
   { 0x9a, 0xdb, 0xf1, 0x99, 0x0d, 0x77, 0xcf, 0xfb } }
 
 #define NS_PIDOMWINDOWOUTER_IID \
   { 0x769693d4, 0xb009, 0x4fe2, \
@@ -935,16 +960,18 @@ public:
 
   float GetAudioVolume() const;
   nsresult SetAudioVolume(float aVolume);
 
   void SetServiceWorkersTestingEnabled(bool aEnabled);
   bool GetServiceWorkersTestingEnabled();
 
   float GetDevicePixelRatio(mozilla::dom::CallerType aCallerType);
+
+  void SetLargeAllocStatus(mozilla::dom::LargeAllocStatus aStatus);
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowOuter, NS_PIDOMWINDOWOUTER_IID)
 
 #include "nsPIDOMWindowInlines.h"
 
 #ifdef MOZILLA_INTERNAL_API
 PopupControlState
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -80,16 +80,17 @@ DIRS += [
     'ipc',
     'workers',
     'audiochannel',
     'broadcastchannel',
     'messagechannel',
     'promise',
     'smil',
     'url',
+    'webauthn',
     'webidl',
     'xbl',
     'xml',
     'xslt',
     'xul',
     'manifest',
     'vr',
     'u2f',
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -47,10 +47,12 @@ tags = openwindow
 skip-if = toolkit == 'android'  || (os == "linux" && debug) # see bug 1261495 for Linux debug time outs
 support-files =
   test_new_window_from_content_child.html
 [browser_test_toolbars_visibility.js]
 support-files =
   test_new_window_from_content_child.html
 [browser_xhr_sandbox.js]
 [browser_prerendering.js]
-support-files = prerender.html
+support-files =
+  prerender.html
+  prerender_target.html
 skip-if = !e10s # Prerendering requires e10s
--- a/dom/tests/browser/browser_prerendering.js
+++ b/dom/tests/browser/browser_prerendering.js
@@ -1,9 +1,23 @@
 const URL = "https://example.com/browser/dom/tests/browser/prerender.html";
+const PRERENDERED_URL = "https://example.com/browser/dom/tests/browser/prerender_target.html";
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
+
+// Returns a promise which resolves to whether or not PRERENDERED_URL has been visited.
+function prerenderedVisited() {
+  let uri = Services.io.newURI(PRERENDERED_URL, null, null);
+  return new Promise(resolve => {
+    PlacesUtils.asyncHistory.isURIVisited(uri, (aUri, aIsVisited) => {
+      resolve(aIsVisited);
+    });
+  });
+}
 
 // Wait for a process change and then fulfil the promise.
 function awaitProcessChange(browser) {
   return new Promise(resolve => {
     browser.addEventListener("BrowserChangedProcess", function bcp(e) {
       browser.removeEventListener("BrowserChangedProcess", bcp);
       info("The browser changed process!");
       resolve();
@@ -33,60 +47,63 @@ add_task(function* () {
           ["dom.linkPrerender.enabled", true],
           ["dom.require_user_interaction_for_beforeunload", false]]
   });
 });
 
 // Test 1: Creating a prerendered browser, and clicking on a link to that browser,
 // will cause changes.
 add_task(function* () {
+  yield PlacesUtils.history.clear();
+
   let tab = gBrowser.addTab(URL);
 
+  is(yield prerenderedVisited(), false, "Should not have been visited");
+
   yield new Promise(resolve => {
     tab.linkedBrowser.messageManager.addMessageListener("Prerender:Request", function f() {
       tab.linkedBrowser.messageManager.removeMessageListener("Prerender:Request", f);
       info("Successfully received the prerender request");
       resolve();
     });
   });
   yield BrowserTestUtils.switchTab(gBrowser, tab);
 
+  is(yield prerenderedVisited(), false, "Should not have been visited");
+
   // Check that visibilityState is set correctly in the prerendered tab. We
   // check all of the tabs because we have no easy way to tell which one is
   // which.
-  let result;
+  let foundTab;
   for (let i = 0; i < gBrowser.tabs.length; ++i) {
-    result = yield ContentTask.spawn(gBrowser.tabs[i].linkedBrowser, null, function* () {
-      let interesting = content.document.location.toString() == "data:text/html,b";
-      if (interesting) {
-        is(content.document.visibilityState, "prerender");
-      }
-      return interesting;
-    });
-    if (result) {
+    foundTab = yield ContentTask.spawn(gBrowser.tabs[i].linkedBrowser, null,
+                                       () => content.document.visibilityState == "prerender");
+    if (foundTab) {
       break;
     }
   }
-  ok(result, "The prerender tab was found!");
+  ok(foundTab, "The prerender tab was found!");
 
   let dialogShown = awaitAndCloseBeforeUnloadDialog();
   ContentTask.spawn(tab.linkedBrowser, null, function*() {
     let anchor = content.document.querySelector("a");
     anchor.click();
   });
   yield dialogShown;
 
   yield awaitProcessChange(tab.linkedBrowser);
 
-  yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
-    is(content.document.location.toString(), "data:text/html,b");
-    is(content.document.visibilityState, "visible",
-       "VisibilityState of formerly prerendered window must be visible");
+  yield ContentTask.spawn(tab.linkedBrowser, PRERENDERED_URL, function*(PRERENDERED_URL) {
+    is(content.document.location.toString(), PRERENDERED_URL);
+    isnot(content.document.visibilityState, "prerender",
+          "VisibilityState of formerly prerendered window must not be prerender");
   });
 
+  is(yield prerenderedVisited(), true, "Should have been visited");
+
   let groupedSHistory = tab.linkedBrowser.frameLoader.groupedSHistory;
   is(groupedSHistory.count, 2, "Check total length of grouped shistory.");
   is(gBrowser.tabs.length, 3, "Check number of opened tabs.");
 
   // We don't touch the about:blank tab opened when browser mochitest runs.
   // The tabs under test are the other 2 tabs, so we're expecting 2 TabClose.
   let closed = new Promise(resolve => {
     let seen = 0;
@@ -96,24 +113,30 @@ add_task(function* () {
         resolve();
       }
     });
   });
 
   yield BrowserTestUtils.removeTab(tab);
   yield closed;
 
+  is(yield prerenderedVisited(), true, "Should have been visited");
+
   is(gBrowser.tabs.length, 1);
 });
 
 // Test 2: Creating a prerendered browser, and navigating to a different url,
 // succeeds, and closes the prerendered browser.
 add_task(function* () {
+  yield PlacesUtils.history.clear();
+
   let tab = gBrowser.addTab(URL);
 
+  is(yield prerenderedVisited(), false, "Should not have been visited");
+
   yield Promise.all([
     BrowserTestUtils.browserLoaded(tab.linkedBrowser),
     new Promise(resolve => {
       tab.linkedBrowser.messageManager.addMessageListener("Prerender:Request", function f() {
         tab.linkedBrowser.messageManager.removeMessageListener("Prerender:Request", f);
         info("Successfully received the prerender request");
         resolve();
       });
@@ -124,16 +147,18 @@ add_task(function* () {
   let dialogShown = awaitAndCloseBeforeUnloadDialog();
   ContentTask.spawn(tab.linkedBrowser, null, function*() {
     let anchor = content.document.querySelector("a");
     anchor.setAttribute("href", "data:text/html,something_else");
     anchor.click();
   });
   yield dialogShown;
 
+  is(yield prerenderedVisited(), false, "Should not have been visited");
+
   yield Promise.all([
     BrowserTestUtils.browserLoaded(tab.linkedBrowser),
     new Promise(resolve => {
       let seen = false;
       gBrowser.tabContainer.addEventListener("TabClose", function f() {
         gBrowser.tabContainer.removeEventListener("TabClose", f);
         if (!seen) {
           seen = true;
@@ -143,10 +168,12 @@ add_task(function* () {
       });
     }),
   ]);
 
   yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
     is(content.document.location.toString(), "data:text/html,something_else");
   });
 
+  is(yield prerenderedVisited(), false, "Should not have been visited");
+
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/dom/tests/browser/prerender.html
+++ b/dom/tests/browser/prerender.html
@@ -1,7 +1,7 @@
 <html>
 <head><title>Prerender Test</title></head>
 <body onbeforeunload="return 'stay with me';">
-<link rel="prerender" href="data:text/html,b">
-<a href="data:text/html,b">clicky here</a>
+<link rel="prerender" href="https://example.com/browser/dom/tests/browser/prerender_target.html">
+<a href="https://example.com/browser/dom/tests/browser/prerender_target.html">clicky here</a>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/prerender_target.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<body>
+    <p>This is just a random HTML document which will be loaded as a target for prerendering.</p>
+</body>
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -37,17 +37,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(U2F)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
 
-static mozilla::LazyLogModule gWebauthLog("webauth_u2f");
+static mozilla::LazyLogModule gU2FLog("u2f");
 
 static nsresult
 AssembleClientData(const nsAString& aOrigin, const nsAString& aTyp,
                    const nsAString& aChallenge, CryptoBuffer& aClientData)
 {
   MOZ_ASSERT(NS_IsMainThread());
   U2FClientData clientDataObject;
   clientDataObject.mTyp.Construct(aTyp); // "Typ" from the U2F specification
@@ -76,47 +76,47 @@ U2FStatus::~U2FStatus()
 {}
 
 void
 U2FStatus::WaitGroupAdd()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   mCount += 1;
-  MOZ_LOG(gWebauthLog, LogLevel::Debug,
+  MOZ_LOG(gU2FLog, LogLevel::Debug,
           ("U2FStatus::WaitGroupAdd, now %d", mCount));
 }
 
 void
 U2FStatus::WaitGroupDone()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   MOZ_ASSERT(mCount > 0);
   mCount -= 1;
-  MOZ_LOG(gWebauthLog, LogLevel::Debug,
+  MOZ_LOG(gU2FLog, LogLevel::Debug,
           ("U2FStatus::WaitGroupDone, now %d", mCount));
   if (mCount == 0) {
     mReentrantMonitor.NotifyAll();
   }
 }
 
 void
 U2FStatus::WaitGroupWait()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  MOZ_LOG(gWebauthLog, LogLevel::Debug,
+  MOZ_LOG(gU2FLog, LogLevel::Debug,
           ("U2FStatus::WaitGroupWait, now %d", mCount));
 
   while (mCount > 0) {
     mReentrantMonitor.Wait();
   }
 
   MOZ_ASSERT(mCount == 0);
-  MOZ_LOG(gWebauthLog, LogLevel::Debug,
+  MOZ_LOG(gU2FLog, LogLevel::Debug,
           ("U2FStatus::Wait completed, now count=%d stopped=%d", mCount,
            mIsStopped));
 }
 
 void
 U2FStatus::Stop(const ErrorCode aErrorCode)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
@@ -622,17 +622,17 @@ U2FRegisterRunnable::Run()
     }
 
     // Treat each call to Promise::All as a work unit, as it completes together
     status->WaitGroupAdd();
 
     U2FPrepPromise::All(AbstractThread::MainThread(), prepPromiseList)
     ->Then(AbstractThread::MainThread(), __func__,
       [status] (const nsTArray<Authenticator>& aTokens) {
-        MOZ_LOG(gWebauthLog, LogLevel::Debug,
+        MOZ_LOG(gU2FLog, LogLevel::Debug,
                 ("ALL: None of the RegisteredKeys were recognized. n=%d",
                  aTokens.Length()));
 
         status->WaitGroupDone();
       },
       [status] (ErrorCode aErrorCode) {
         status->Stop(aErrorCode);
         status->WaitGroupDone();
@@ -779,17 +779,17 @@ U2FSignRunnable::U2FSignRunnable(const n
 
     mRegisteredKeys.AppendElement(localKey);
   }
 
   // Assemble a clientData object
   nsresult rv = AssembleClientData(aOrigin, kGetAssertion, aChallenge,
                                    mClientData);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    MOZ_LOG(gWebauthLog, LogLevel::Warning,
+    MOZ_LOG(gU2FLog, LogLevel::Warning,
             ("Failed to AssembleClientData for the U2FSignRunnable."));
     return;
   }
 }
 
 U2FSignRunnable::~U2FSignRunnable()
 {
   nsNSSShutDownPreventionLock locker;
@@ -971,25 +971,25 @@ U2F::Init(nsPIDOMWindowInner* aParent, E
   }
 
   if (NS_WARN_IF(mOrigin.IsEmpty())) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (!EnsureNSSInitializedChromeOrContent()) {
-    MOZ_LOG(gWebauthLog, LogLevel::Debug,
+    MOZ_LOG(gU2FLog, LogLevel::Debug,
             ("Failed to get NSS context for U2F"));
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   // This only functions in e10s mode
   if (XRE_IsParentProcess()) {
-    MOZ_LOG(gWebauthLog, LogLevel::Debug,
+    MOZ_LOG(gU2FLog, LogLevel::Debug,
             ("Is non-e10s Process, U2F not available"));
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   // Monolithically insert compatible nsIU2FToken objects into mAuthenticators.
   // In future functionality expansions, this is where we could add a dynamic
   // add/remove interface.
--- a/dom/u2f/U2F.h
+++ b/dom/u2f/U2F.h
@@ -5,31 +5,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_U2F_h
 #define mozilla_dom_U2F_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/CryptoBuffer.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/U2FBinding.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/SharedThreadPool.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIU2FToken.h"
 #include "nsNSSShutDown.h"
 #include "nsPIDOMWindow.h"
 #include "nsProxyRelease.h"
 #include "nsWrapperCache.h"
 
 #include "U2FAuthenticator.h"
-#include "USBToken.h"
 
 namespace mozilla {
 namespace dom {
 
 class U2FRegisterCallback;
 class U2FSignCallback;
 
 // Defined in U2FBinding.h by the U2F.webidl; their use requires a JSContext.
--- a/dom/u2f/U2FAuthenticator.h
+++ b/dom/u2f/U2FAuthenticator.h
@@ -3,17 +3,16 @@
 /* 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/. */
 
 #ifndef mozilla_dom_U2FAuthenticator_h
 #define mozilla_dom_U2FAuthenticator_h
 
 #include "nsIU2FToken.h"
-#include "USBToken.h"
 
 namespace mozilla {
 namespace dom {
 
  // These enumerations are defined in the FIDO U2F Javascript API under the
 // interface "ErrorCode" as constant integers, and thus in the U2F.webidl file.
 // Any changes to these must occur in both locations.
 enum class ErrorCode {
deleted file mode 100644
--- a/dom/u2f/USBToken.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#include "USBToken.h"
-
-namespace mozilla {
-namespace dom {
-
-USBToken::USBToken()
-  : mInitialized(false)
-{}
-
-USBToken::~USBToken()
-{}
-
-nsresult
-USBToken::Init()
-{
-  // This routine does nothing at present, but Bug 1245527 will
-  // integrate the upcoming USB HID service here, which will likely
-  // require an initialization upon load.
-  MOZ_ASSERT(!mInitialized);
-  if (mInitialized) {
-    return NS_OK;
-  }
-
-  mInitialized = true;
-  return NS_OK;
-}
-
-const nsString USBToken::mVersion = NS_LITERAL_STRING("U2F_V2");
-
-bool
-USBToken::IsCompatibleVersion(const nsString& aVersionParam) const
-{
-  MOZ_ASSERT(mInitialized);
-  return mVersion == aVersionParam;
-}
-
-bool
-USBToken::IsRegistered(const CryptoBuffer& aKeyHandle) const
-{
-  MOZ_ASSERT(mInitialized);
-  return false;
-}
-
-nsresult
-USBToken::Register(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
-                   const CryptoBuffer& /* aChallengeParam */,
-                   const CryptoBuffer& /* aApplicationParam */,
-                   CryptoBuffer& aRegistrationData) const
-{
-  MOZ_ASSERT(mInitialized);
-  return NS_ERROR_NOT_AVAILABLE;
-}
-
-nsresult
-USBToken::Sign(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
-               const CryptoBuffer& aApplicationParam,
-               const CryptoBuffer& aChallengeParam,
-               const CryptoBuffer& aKeyHandle,
-               CryptoBuffer& aSignatureData) const
-{
-  MOZ_ASSERT(mInitialized);
-  return NS_ERROR_NOT_AVAILABLE;
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/u2f/USBToken.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#ifndef mozilla_dom_USBToken_h
-#define mozilla_dom_USBToken_h
-
-#include "mozilla/dom/CryptoBuffer.h"
-
-namespace mozilla {
-namespace dom {
-
-// USBToken implements FIDO operations using a USB device.
-class USBToken final
-{
-public:
-  USBToken();
-
-  ~USBToken();
-
-  nsresult Init();
-
-  bool IsCompatibleVersion(const nsString& aVersionParam) const;
-
-  bool IsRegistered(const CryptoBuffer& aKeyHandle) const;
-
-  nsresult Register(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
-                    const CryptoBuffer& aApplicationParam,
-                    const CryptoBuffer& aChallengeParam,
-                    CryptoBuffer& aRegistrationData) const;
-
-  nsresult Sign(const Optional<Nullable<int32_t>>& opt_timeoutSeconds,
-                const CryptoBuffer& aApplicationParam,
-                const CryptoBuffer& aChallengeParam,
-                const CryptoBuffer& aKeyHandle,
-                CryptoBuffer& aSignatureData) const;
-
-private:
-  bool mInitialized;
-
-  static const nsString mVersion;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_USBToken_h
deleted file mode 100644
--- a/dom/u2f/WebAuthentication.cpp
+++ /dev/null
@@ -1,1054 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#include "mozilla/dom/WebAuthentication.h"
-#include "mozilla/dom/WebAuthnAssertion.h"
-#include "mozilla/dom/WebAuthnAttestation.h"
-
-#include "mozilla/dom/Promise.h"
-#include "nsICryptoHash.h"
-#include "pkix/Input.h"
-#include "pkixutil.h"
-
-namespace mozilla {
-namespace dom {
-
-extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
-
-// Only needed for refcounted objects.
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthentication, mParent)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthentication)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthentication)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthentication)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-template<class OOS>
-static nsresult
-GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
-                 /* out */ nsString& aName)
-{
-  MOZ_ASSERT(aAlgorithm.IsString()); // TODO: remove assertion when we coerce.
-
-  if (aAlgorithm.IsString()) {
-    // If string, then treat as algorithm name
-    aName.Assign(aAlgorithm.GetAsString());
-  } else {
-    // TODO: Coerce to string and extract name. See WebCryptoTask.cpp
-  }
-
-  if (!NormalizeToken(aName, aName)) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  return NS_OK;
-}
-
-static nsresult
-HashCString(nsICryptoHash* aHashService, const nsACString& aIn,
-            /* out */ CryptoBuffer& aOut)
-{
-  MOZ_ASSERT(aHashService);
-
-  nsresult rv = aHashService->Init(nsICryptoHash::SHA256);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = aHashService->Update(
-         reinterpret_cast<const uint8_t*>(aIn.BeginReading()),aIn.Length());
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsAutoCString fullHash;
-  // Passing false below means we will get a binary result rather than a
-  // base64-encoded string.
-  rv = aHashService->Finish(false, fullHash);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  aOut.Assign(fullHash);
-  return rv;
-}
-
-static nsresult
-AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge,
-                   /* out */ nsACString& aJsonOut)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsString challengeBase64;
-  nsresult rv = aChallenge.ToJwkBase64(challengeBase64);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  WebAuthnClientData clientDataObject;
-  clientDataObject.mOrigin.Assign(aOrigin);
-  clientDataObject.mHashAlg.SetAsString().Assign(NS_LITERAL_STRING("S256"));
-  clientDataObject.mChallenge.Assign(challengeBase64);
-
-  nsAutoString temp;
-  if (NS_WARN_IF(!clientDataObject.ToJSON(temp))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  aJsonOut.Assign(NS_ConvertUTF16toUTF8(temp));
-  return NS_OK;
-}
-
-static nsresult
-ScopedCredentialGetData(const ScopedCredentialDescriptor& aSCD,
-                        /* out */ uint8_t** aBuf, /* out */ uint32_t* aBufLen)
-{
-  MOZ_ASSERT(aBuf);
-  MOZ_ASSERT(aBufLen);
-
-  if (aSCD.mId.IsArrayBufferView()) {
-    const ArrayBufferView& view = aSCD.mId.GetAsArrayBufferView();
-    view.ComputeLengthAndData();
-    *aBuf = view.Data();
-    *aBufLen = view.Length();
-  } else if (aSCD.mId.IsArrayBuffer()) {
-    const ArrayBuffer& buffer = aSCD.mId.GetAsArrayBuffer();
-    buffer.ComputeLengthAndData();
-    *aBuf = buffer.Data();
-    *aBufLen = buffer.Length();
-  } else {
-    MOZ_ASSERT(false);
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
-static nsresult
-ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
-                   uint32_t aLen)
-{
-  if (aSrc.EnsureLength(aLen) != pkix::Success) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-
-  aDest.ClearAndRetainStorage();
-
-  for (uint32_t offset = 0; offset < aLen; ++offset) {
-    uint8_t b;
-    if (aSrc.Read(b) != pkix::Success) {
-      return NS_ERROR_DOM_UNKNOWN_ERR;
-    }
-    if (!aDest.AppendElement(b, mozilla::fallible)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  return NS_OK;
-}
-
-static nsresult
-U2FAssembleAuthenticatorData(/* out */ CryptoBuffer& aAuthenticatorData,
-                             const CryptoBuffer& aRpIdHash,
-                             const CryptoBuffer& aSignatureData)
-{
-  // The AuthenticatorData for U2F devices is the concatenation of the
-  // RP ID with the output of the U2F Sign operation.
-  if (aRpIdHash.Length() != 32) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  if (!aAuthenticatorData.AppendElements(aRpIdHash, mozilla::fallible)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  if (!aAuthenticatorData.AppendElements(aSignatureData, mozilla::fallible)) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return NS_OK;
-}
-
-static nsresult
-U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
-                                 /* out */ CryptoBuffer& aPubKeyBuf,
-                                 /* out */ CryptoBuffer& aKeyHandleBuf,
-                                 /* out */ CryptoBuffer& aAttestationCertBuf,
-                                 /* out */ CryptoBuffer& aSignatureBuf)
-{
-  // U2F v1.1 Format via
-  // http://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html
-  //
-  // Bytes  Value
-  // 1      0x05
-  // 65     public key
-  // 1      key handle length
-  // *      key handle
-  // ASN.1  attestation certificate
-  // *      attestation signature
-
-  pkix::Input u2fResponse;
-  u2fResponse.Init(aResponse.Elements(), aResponse.Length());
-
-  pkix::Reader input(u2fResponse);
-
-  uint8_t b;
-  if (input.Read(b) != pkix::Success) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-  if (b != 0x05) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-
-  nsresult rv = ReadToCryptoBuffer(input, aPubKeyBuf, 65);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  uint8_t handleLen;
-  if (input.Read(handleLen) != pkix::Success) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-
-  rv = ReadToCryptoBuffer(input, aKeyHandleBuf, handleLen);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  // We have to parse the ASN.1 SEQUENCE on the outside to determine the cert's
-  // length.
-  pkix::Input cert;
-  if (pkix::der::ExpectTagAndGetValue(input, pkix::der::SEQUENCE, cert)
-        != pkix::Success) {
-    return NS_ERROR_DOM_UNKNOWN_ERR;
-  }
-
-  pkix::Reader certInput(cert);
-  rv = ReadToCryptoBuffer(certInput, aAttestationCertBuf, cert.GetLength());
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  // The remainder of u2fResponse is the signature
-  pkix::Input u2fSig;
-  input.SkipToEnd(u2fSig);
-  pkix::Reader sigInput(u2fSig);
-  rv = ReadToCryptoBuffer(sigInput, aSignatureBuf, u2fSig.GetLength());
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-WebAuthentication::WebAuthentication(nsPIDOMWindowInner* aParent)
-  : mInitialized(false)
-{
-  mParent = do_QueryInterface(aParent);
-  MOZ_ASSERT(mParent);
-}
-
-WebAuthentication::~WebAuthentication()
-{}
-
-nsresult
-WebAuthentication::InitLazily()
-{
-  if (mInitialized) {
-    return NS_OK;
-  }
-
-  MOZ_ASSERT(mParent);
-  if (!mParent) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIDocument> doc = mParent->GetDoc();
-  MOZ_ASSERT(doc);
-
-  nsIPrincipal* principal = doc->NodePrincipal();
-  nsresult rv = nsContentUtils::GetUTFOrigin(principal, mOrigin);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (NS_WARN_IF(mOrigin.IsEmpty())) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // This only functions in e10s mode
-  // TODO: Remove in Bug 1323339
-  if (XRE_IsParentProcess()) {
-    MOZ_LOG(gWebauthLog, LogLevel::Debug,
-            ("Is non-e10s Process, WebAuthn not available"));
-    return NS_ERROR_FAILURE;
-  }
-
-  if (Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED)) {
-    if (!mAuthenticators.AppendElement(new NSSU2FTokenRemote(),
-                                       mozilla::fallible)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  mInitialized = true;
-  return NS_OK;
-}
-
-JSObject*
-WebAuthentication::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return WebAuthenticationBinding::Wrap(aCx, this, aGivenProto);
-}
-
-// NOTE: This method represents a theoretical way to use a U2F-compliant token
-// to produce the result of the WebAuthn MakeCredential method. The exact
-// mapping of U2F data fields to WebAuthn data fields is still a matter of
-// ongoing discussion, and this should not be taken as anything but a point-in-
-// time possibility.
-void
-WebAuthentication::U2FAuthMakeCredential(
-             const RefPtr<CredentialRequest>& aRequest,
-             const Authenticator& aToken, CryptoBuffer& aRpIdHash,
-             const nsACString& aClientData, CryptoBuffer& aClientDataHash,
-             const Account& aAccount,
-             const nsTArray<ScopedCredentialParameters>& aNormalizedParams,
-             const Optional<Sequence<ScopedCredentialDescriptor>>& aExcludeList,
-             const WebAuthnExtensions& aExtensions)
-{
-  MOZ_LOG(gWebauthLog, LogLevel::Debug, ("U2FAuthMakeCredential"));
-  aRequest->AddActiveToken(__func__);
-
-  // 5.1.1 When this operation is invoked, the authenticator must perform the
-  // following procedure:
-
-  // 5.1.1.a Check if all the supplied parameters are syntactically well-
-  // formed and of the correct length. If not, return an error code equivalent
-  // to UnknownError and terminate the operation.
-
-  if ((aRpIdHash.Length() != SHA256_LENGTH) ||
-      (aClientDataHash.Length() != SHA256_LENGTH)) {
-    aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
-    return;
-  }
-
-  // 5.1.1.b Check if at least one of the specified combinations of
-  // ScopedCredentialType and cryptographic parameters is supported. If not,
-  // return an error code equivalent to NotSupportedError and terminate the
-  // operation.
-
-  bool isValidCombination = false;
-
-  for (size_t a = 0; a < aNormalizedParams.Length(); ++a) {
-    if (aNormalizedParams[a].mType == ScopedCredentialType::ScopedCred &&
-        aNormalizedParams[a].mAlgorithm.IsString() &&
-        aNormalizedParams[a].mAlgorithm.GetAsString().EqualsLiteral(
-          WEBCRYPTO_NAMED_CURVE_P256)) {
-      isValidCombination = true;
-      break;
-    }
-  }
-  if (!isValidCombination) {
-    aRequest->SetFailure(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return;
-  }
-
-  // 5.1.1.c Check if a credential matching any of the supplied
-  // ScopedCredential identifiers is present on this authenticator. If so,
-  // return an error code equivalent to NotAllowedError and terminate the
-  // operation.
-
-  if (aExcludeList.WasPassed()) {
-    const Sequence<ScopedCredentialDescriptor>& list = aExcludeList.Value();
-
-    for (const ScopedCredentialDescriptor& scd : list) {
-      bool isRegistered = false;
-
-      uint8_t *data;
-      uint32_t len;
-
-      // data is owned by the Descriptor, do don't free it here.
-      if (NS_FAILED(ScopedCredentialGetData(scd, &data, &len))) {
-        aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
-        return;
-      }
-
-      nsresult rv = aToken->IsRegistered(data, len, &isRegistered);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        aRequest->SetFailure(rv);
-        return;
-      }
-
-      if (isRegistered) {
-        aRequest->SetFailure(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-        return;
-      }
-    }
-  }
-
-  // 5.1.1.d Prompt the user for consent to create a new credential. The
-  // prompt for obtaining this consent is shown by the authenticator if it has
-  // its own output capability, or by the user agent otherwise. If the user
-  // denies consent, return an error code equivalent to NotAllowedError and
-  // terminate the operation.
-
-  // 5.1.1.d Once user consent has been obtained, generate a new credential
-  // object
-
-  // 5.1.1.e If any error occurred while creating the new credential object,
-  // return an error code equivalent to UnknownError and terminate the
-  // operation.
-
-  // 5.1.1.f Process all the supported extensions requested by the client, and
-  // generate an attestation statement. If no authority key is available to
-  // sign such an attestation statement, then the authenticator performs self
-  // attestation of the credential with its own private key. For more details
-  // on attestation, see §5.3 Credential Attestation Statements.
-
-  // No extensions are supported
-
-  // 4.1.1.11 While issuedRequests is not empty, perform the following actions
-  // depending upon the adjustedTimeout timer and responses from the
-  // authenticators:
-
-  // 4.1.1.11.a If the adjustedTimeout timer expires, then for each entry in
-  // issuedRequests invoke the authenticatorCancel operation on that
-  // authenticator and remove its entry from the list.
-
-  uint8_t* buffer;
-  uint32_t bufferlen;
-
-  nsresult rv = aToken->Register(aRpIdHash.Elements(), aRpIdHash.Length(),
-                                 aClientDataHash.Elements(),
-                                 aClientDataHash.Length(), &buffer, &bufferlen);
-
-  // 4.1.1.11.b If any authenticator returns a status indicating that the user
-  // cancelled the operation, delete that authenticator’s entry from
-  // issuedRequests. For each remaining entry in issuedRequests invoke the
-  // authenticatorCancel operation on that authenticator and remove its entry
-  // from the list.
-
-  // 4.1.1.11.c If any authenticator returns an error status, delete the
-  // corresponding entry from issuedRequests.
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
-    return;
-  }
-
-  MOZ_ASSERT(buffer);
-  CryptoBuffer regData;
-  if (NS_WARN_IF(!regData.Assign(buffer, bufferlen))) {
-    free(buffer);
-    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
-  free(buffer);
-
-  // Decompose the U2F registration packet
-  CryptoBuffer pubKeyBuf;
-  CryptoBuffer keyHandleBuf;
-  CryptoBuffer attestationCertBuf;
-  CryptoBuffer signatureBuf;
-
-  rv = U2FDecomposeRegistrationResponse(regData, pubKeyBuf, keyHandleBuf,
-                                        attestationCertBuf, signatureBuf);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRequest->SetFailure(rv);
-    return;
-  }
-
-  // Sign the aClientDataHash explicitly to get the format needed for
-  // the AuthenticatorData parameter of WebAuthnAttestation. This might
-  // be temporary while the spec settles down how to incorporate U2F.
-  rv = aToken->Sign(aRpIdHash.Elements(), aRpIdHash.Length(),
-                    aClientDataHash.Elements(), aClientDataHash.Length(),
-                    keyHandleBuf.Elements(), keyHandleBuf.Length(), &buffer,
-                    &bufferlen);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRequest->SetFailure(rv);
-    return;
-  }
-
-  MOZ_ASSERT(buffer);
-  CryptoBuffer signatureData;
-  if (NS_WARN_IF(!signatureData.Assign(buffer, bufferlen))) {
-    free(buffer);
-    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
-  free(buffer);
-
-  CryptoBuffer clientDataBuf;
-  if (!clientDataBuf.Assign(aClientData)) {
-    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-    return;
-  }
-
-  CryptoBuffer authenticatorDataBuf;
-  rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, aRpIdHash,
-                                    signatureData);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    aRequest->SetFailure(rv);
-    return;
-  }
-
-  // 4.1.1.11.d If any authenticator indicates success:
-
-  // 4.1.1.11.d.1 Remove this authenticator’s entry from issuedRequests.
-
-  // 4.1.1.11.d.2 Create a new ScopedCredentialInfo object named value and
-  // populate its fields with the values returned from the authenticator as well
-  // as the clientDataJSON computed earlier.
-
-  RefPtr<ScopedCredential> credential = new ScopedCredential(this);
-  credential->SetType(ScopedCredentialType::ScopedCred);
-  credential->SetId(keyHandleBuf);
-
-  RefPtr<WebAuthnAttestation> attestation = new WebAuthnAttestation(this);
-  attestation->SetFormat(NS_LITERAL_STRING("u2f"));
-  attestation->SetClientData(clientDataBuf);
-  attestation->SetAuthenticatorData(authenticatorDataBuf);
-  attestation->SetAttestation(regData);
-
-  CredentialPtr info = new ScopedCredentialInfo(this);
-  info->SetCredential(credential);
-  info->SetAttestation(attestation);
-
-  // 4.1.1.11.d.3 For each remaining entry in issuedRequests invoke the
-  // authenticatorCancel operation on that authenticator and remove its entry
-  // from the list.
-
-  // 4.1.1.11.d.4 Resolve promise with value and terminate this algorithm.
-  aRequest->SetSuccess(info);
-}
-
-// NOTE: This method represents a theoretical way to use a U2F-compliant token
-// to produce the result of the WebAuthn GetAssertion method. The exact mapping
-// of U2F data fields to WebAuthn data fields is still a matter of ongoing
-// discussion, and this should not be taken as anything but a point-in- time
-// possibility.
-void
-WebAuthentication::U2FAuthGetAssertion(const RefPtr<AssertionRequest>& aRequest,
-                    const Authenticator& aToken, CryptoBuffer& aRpIdHash,
-                    const nsACString& aClientData, CryptoBuffer& aClientDataHash,
-                    nsTArray<CryptoBuffer>& aAllowList,
-                    const WebAuthnExtensions& aExtensions)
-{
-  MOZ_LOG(gWebauthLog, LogLevel::Debug, ("U2FAuthGetAssertion"));
-
-  // 4.1.2.7.e Add an entry to issuedRequests, corresponding to this request.
-  aRequest->AddActiveToken(__func__);
-
-  // 4.1.2.8 While issuedRequests is not empty, perform the following actions
-  // depending upon the adjustedTimeout timer and responses from the
-  // authenticators:
-
-  // 4.1.2.8.a If the timer for adjustedTimeout expires, then for each entry
-  // in issuedRequests invoke the authenticatorCancel operation on that
-  // authenticator and remove its entry from the list.
-
-  for (CryptoBuffer& allowedCredential : aAllowList) {
-    bool isRegistered = false;
-    nsresult rv = aToken->IsRegistered(allowedCredential.Elements(),
-                                       allowedCredential.Length(),
-                                       &isRegistered);
-
-    // 4.1.2.8.b If any authenticator returns a status indicating that the user
-    // cancelled the operation, delete that authenticator’s entry from
-    // issuedRequests. For each remaining entry in issuedRequests invoke the
-    // authenticatorCancel operation on that authenticator, and remove its entry
-    // from the list.
-
-    // 4.1.2.8.c If any authenticator returns an error status, delete the
-    // corresponding entry from issuedRequests.
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRequest->SetFailure(rv);
-      return;
-    }
-
-    if (!isRegistered) {
-      continue;
-    }
-
-    // Sign
-    uint8_t* buffer;
-    uint32_t bufferlen;
-    rv = aToken->Sign(aRpIdHash.Elements(), aRpIdHash.Length(),
-                      aClientDataHash.Elements(), aClientDataHash.Length(),
-                      allowedCredential.Elements(), allowedCredential.Length(),
-                      &buffer, &bufferlen);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRequest->SetFailure(rv);
-      return;
-    }
-
-    MOZ_ASSERT(buffer);
-    CryptoBuffer signatureData;
-    if (NS_WARN_IF(!signatureData.Assign(buffer, bufferlen))) {
-      free(buffer);
-      aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
-    free(buffer);
-
-    // 4.1.2.8.d If any authenticator returns success:
-
-    // 4.1.2.8.d.1 Remove this authenticator’s entry from issuedRequests.
-
-    // 4.1.2.8.d.2 Create a new WebAuthnAssertion object named value and
-    // populate its fields with the values returned from the authenticator as
-    // well as the clientDataJSON computed earlier.
-
-    CryptoBuffer clientDataBuf;
-    if (!clientDataBuf.Assign(aClientData)) {
-      aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
-
-    CryptoBuffer authenticatorDataBuf;
-    rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, aRpIdHash,
-                                      signatureData);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRequest->SetFailure(rv);
-      return;
-    }
-
-    RefPtr<ScopedCredential> credential = new ScopedCredential(this);
-    credential->SetType(ScopedCredentialType::ScopedCred);
-    credential->SetId(allowedCredential);
-
-    AssertionPtr assertion = new WebAuthnAssertion(this);
-    assertion->SetCredential(credential);
-    assertion->SetClientData(clientDataBuf);
-    assertion->SetAuthenticatorData(authenticatorDataBuf);
-    assertion->SetSignature(signatureData);
-
-    // 4.1.2.8.d.3 For each remaining entry in issuedRequests invoke the
-    // authenticatorCancel operation on that authenticator and remove its entry
-    // from the list.
-
-    // 4.1.2.8.d.4 Resolve promise with value and terminate this algorithm.
-    aRequest->SetSuccess(assertion);
-    return;
-  }
-
-  // 4.1.2.9 Reject promise with a DOMException whose name is "NotAllowedError",
-  // and terminate this algorithm.
-  aRequest->SetFailure(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-}
-
-nsresult
-WebAuthentication::RelaxSameOrigin(const nsAString& aInputRpId,
-                                   /* out */ nsACString& aRelaxedRpId)
-{
-  MOZ_ASSERT(mParent);
-  nsCOMPtr<nsIDocument> document = mParent->GetDoc();
-  if (!document || !document->IsHTMLDocument()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // TODO: Bug 1329764: Invoke the Relax Algorithm, once properly defined
-  aRelaxedRpId.Assign(NS_ConvertUTF16toUTF8(aInputRpId));
-  return NS_OK;
-}
-
-already_AddRefed<Promise>
-WebAuthentication::MakeCredential(JSContext* aCx, const Account& aAccount,
-                  const Sequence<ScopedCredentialParameters>& aCryptoParameters,
-                  const ArrayBufferViewOrArrayBuffer& aChallenge,
-                  const ScopedCredentialOptions& aOptions)
-{
-  MOZ_ASSERT(mParent);
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
-  if (!global) {
-    return nullptr;
-  }
-
-  ErrorResult rv;
-  RefPtr<Promise> promise = Promise::Create(global, rv);
-
-  nsresult initRv = InitLazily();
-  if (NS_FAILED(initRv)) {
-    promise->MaybeReject(initRv);
-    return promise.forget();
-  }
-
-  // 4.1.1.1 If timeoutSeconds was specified, check if its value lies within a
-  // reasonable range as defined by the platform and if not, correct it to the
-  // closest value lying within that range.
-
-  double adjustedTimeout = 30.0;
-  if (aOptions.mTimeoutSeconds.WasPassed()) {
-    adjustedTimeout = aOptions.mTimeoutSeconds.Value();
-    adjustedTimeout = std::max(15.0, adjustedTimeout);
-    adjustedTimeout = std::min(120.0, adjustedTimeout);
-  }
-
-  // 4.1.1.2 Let promise be a new Promise. Return promise and start a timer for
-  // adjustedTimeout seconds.
-
-  RefPtr<CredentialRequest> requestMonitor = new CredentialRequest();
-  requestMonitor->SetDeadline(TimeDuration::FromSeconds(adjustedTimeout));
-
-  if (mOrigin.EqualsLiteral("null")) {
-    // 4.1.1.3 If callerOrigin is an opaque origin, reject promise with a
-    // DOMException whose name is "NotAllowedError", and terminate this
-    // algorithm
-    MOZ_LOG(gWebauthLog, LogLevel::Debug, ("Rejecting due to opaque origin"));
-    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-    return promise.forget();
-  }
-
-  nsCString rpId;
-  if (!aOptions.mRpId.WasPassed()) {
-    // 4.1.1.3.a If rpId is not specified, then set rpId to callerOrigin, and
-    // rpIdHash to the SHA-256 hash of rpId.
-    rpId.Assign(NS_ConvertUTF16toUTF8(mOrigin));
-  } else {
-    // 4.1.1.3.b If rpId is specified, then invoke the procedure used for
-    // relaxing the same-origin restriction by setting the document.domain
-    // attribute, using rpId as the given value but without changing the current
-    // document’s domain. If no errors are thrown, set rpId to the value of host
-    // as computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
-    // Otherwise, reject promise with a DOMException whose name is
-    // "SecurityError", and terminate this algorithm.
-
-    if (NS_FAILED(RelaxSameOrigin(aOptions.mRpId.Value(), rpId))) {
-      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-      return promise.forget();
-    }
-  }
-
-  CryptoBuffer rpIdHash;
-  if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
-    promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
-    return promise.forget();
-  }
-
-  nsresult srv;
-  nsCOMPtr<nsICryptoHash> hashService =
-    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  srv = HashCString(hashService, rpId, rpIdHash);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  // 4.1.1.4 Process each element of cryptoParameters using the following steps,
-  // to produce a new sequence normalizedParameters.
-  nsTArray<ScopedCredentialParameters> normalizedParams;
-  for (size_t a = 0; a < aCryptoParameters.Length(); ++a) {
-    // 4.1.1.4.a Let current be the currently selected element of
-    // cryptoParameters.
-
-    // 4.1.1.4.b If current.type does not contain a ScopedCredentialType
-    // supported by this implementation, then stop processing current and move
-    // on to the next element in cryptoParameters.
-    if (aCryptoParameters[a].mType != ScopedCredentialType::ScopedCred) {
-      continue;
-    }
-
-    // 4.1.1.4.c Let normalizedAlgorithm be the result of normalizing an
-    // algorithm using the procedure defined in [WebCryptoAPI], with alg set to
-    // current.algorithm and op set to 'generateKey'. If an error occurs during
-    // this procedure, then stop processing current and move on to the next
-    // element in cryptoParameters.
-
-    nsString algName;
-    if (NS_FAILED(GetAlgorithmName(aCx, aCryptoParameters[a].mAlgorithm,
-                                   algName))) {
-      continue;
-    }
-
-    // 4.1.1.4.d Add a new object of type ScopedCredentialParameters to
-    // normalizedParameters, with type set to current.type and algorithm set to
-    // normalizedAlgorithm.
-    ScopedCredentialParameters normalizedObj;
-    normalizedObj.mType = aCryptoParameters[a].mType;
-    normalizedObj.mAlgorithm.SetAsString().Assign(algName);
-
-    if (!normalizedParams.AppendElement(normalizedObj, mozilla::fallible)){
-      promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
-      return promise.forget();
-    }
-  }
-
-  // 4.1.1.5 If normalizedAlgorithm is empty and cryptoParameters was not empty,
-  // cancel the timer started in step 2, reject promise with a DOMException
-  // whose name is "NotSupportedError", and terminate this algorithm.
-  if (normalizedParams.IsEmpty() && !aCryptoParameters.IsEmpty()) {
-    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return promise.forget();
-  }
-
-  // 4.1.1.6 If excludeList is undefined, set it to the empty list.
-
-  // 4.1.1.7 If extensions was specified, process any extensions supported by
-  // this client platform, to produce the extension data that needs to be sent
-  // to the authenticator. If an error is encountered while processing an
-  // extension, skip that extension and do not produce any extension data for
-  // it. Call the result of this processing clientExtensions.
-
-  // Currently no extensions are supported
-
-  // 4.1.1.8 Use attestationChallenge, callerOrigin and rpId, along with the
-  // token binding key associated with callerOrigin (if any), to create a
-  // ClientData structure representing this request. Choose a hash algorithm for
-  // hashAlg and compute the clientDataJSON and clientDataHash.
-
-  CryptoBuffer challenge;
-  if (!challenge.Assign(aChallenge)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  nsAutoCString clientDataJSON;
-  srv = AssembleClientData(mOrigin, challenge, clientDataJSON);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  CryptoBuffer clientDataHash;
-  if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  srv = HashCString(hashService, clientDataJSON, clientDataHash);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  // 4.1.1.9 Initialize issuedRequests to an empty list.
-  RefPtr<CredentialPromise> monitorPromise = requestMonitor->Ensure();
-
-  // 4.1.1.10 For each authenticator currently available on this platform:
-  // asynchronously invoke the authenticatorMakeCredential operation on that
-  // authenticator with rpIdHash, clientDataHash, accountInformation,
-  // normalizedParameters, excludeList and clientExtensions as parameters. Add a
-  // corresponding entry to issuedRequests.
-  for (Authenticator u2ftoken : mAuthenticators) {
-    // 4.1.1.10.a For each credential C in excludeList that has a non-empty
-    // transports list, optionally use only the specified transports to test for
-    // the existence of C.
-    U2FAuthMakeCredential(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
-                          clientDataHash, aAccount, normalizedParams,
-                          aOptions.mExcludeList, aOptions.mExtensions);
-  }
-
-  requestMonitor->CompleteTask();
-
-  monitorPromise->Then(AbstractThread::MainThread(), __func__,
-    [promise] (CredentialPtr aInfo) {
-      promise->MaybeResolve(aInfo);
-    },
-    [promise] (nsresult aErrorCode) {
-      promise->MaybeReject(aErrorCode);
-  });
-
-  return promise.forget();
-}
-
-already_AddRefed<Promise>
-WebAuthentication::GetAssertion(const ArrayBufferViewOrArrayBuffer& aChallenge,
-                                const AssertionOptions& aOptions)
-{
-  MOZ_ASSERT(mParent);
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
-  if (!global) {
-    return nullptr;
-  }
-
-  // 4.1.2.1 If timeoutSeconds was specified, check if its value lies within a
-  // reasonable range as defined by the platform and if not, correct it to the
-  // closest value lying within that range.
-
-  double adjustedTimeout = 30.0;
-  if (aOptions.mTimeoutSeconds.WasPassed()) {
-    adjustedTimeout = aOptions.mTimeoutSeconds.Value();
-    adjustedTimeout = std::max(15.0, adjustedTimeout);
-    adjustedTimeout = std::min(120.0, adjustedTimeout);
-  }
-
-  // 4.1.2.2 Let promise be a new Promise. Return promise and start a timer for
-  // adjustedTimeout seconds.
-
-  RefPtr<AssertionRequest> requestMonitor = new AssertionRequest();
-  requestMonitor->SetDeadline(TimeDuration::FromSeconds(adjustedTimeout));
-
-  ErrorResult rv;
-  RefPtr<Promise> promise = Promise::Create(global, rv);
-
-  nsresult initRv = InitLazily();
-  if (NS_FAILED(initRv)) {
-    promise->MaybeReject(initRv);
-    return promise.forget();
-  }
-
-  if (mOrigin.EqualsLiteral("null")) {
-    // 4.1.2.3 If callerOrigin is an opaque origin, reject promise with a
-    // DOMException whose name is "NotAllowedError", and terminate this algorithm
-    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-    return promise.forget();
-  }
-
-  nsCString rpId;
-  if (!aOptions.mRpId.WasPassed()) {
-    // 4.1.2.3.a If rpId is not specified, then set rpId to callerOrigin, and
-    // rpIdHash to the SHA-256 hash of rpId.
-    rpId.Assign(NS_ConvertUTF16toUTF8(mOrigin));
-  } else {
-    // 4.1.2.3.b If rpId is specified, then invoke the procedure used for
-    // relaxing the same-origin restriction by setting the document.domain
-    // attribute, using rpId as the given value but without changing the current
-    // document’s domain. If no errors are thrown, set rpId to the value of host
-    // as computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
-    // Otherwise, reject promise with a DOMException whose name is
-    // "SecurityError", and terminate this algorithm.
-
-    if (NS_FAILED(RelaxSameOrigin(aOptions.mRpId.Value(), rpId))) {
-      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-      return promise.forget();
-    }
-  }
-
-  CryptoBuffer rpIdHash;
-  if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  nsresult srv;
-  nsCOMPtr<nsICryptoHash> hashService =
-    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  srv = HashCString(hashService, rpId, rpIdHash);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  // 4.1.2.4 If extensions was specified, process any extensions supported by
-  // this client platform, to produce the extension data that needs to be sent
-  // to the authenticator. If an error is encountered while processing an
-  // extension, skip that extension and do not produce any extension data for
-  // it. Call the result of this processing clientExtensions.
-
-  // TODO
-
-  // 4.1.2.5 Use assertionChallenge, callerOrigin and rpId, along with the token
-  // binding key associated with callerOrigin (if any), to create a ClientData
-  // structure representing this request. Choose a hash algorithm for hashAlg
-  // and compute the clientDataJSON and clientDataHash.
-  CryptoBuffer challenge;
-  if (!challenge.Assign(aChallenge)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  nsAutoCString clientDataJSON;
-  srv = AssembleClientData(mOrigin, challenge, clientDataJSON);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  CryptoBuffer clientDataHash;
-  if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  srv = HashCString(hashService, clientDataJSON, clientDataHash);
-  if (NS_WARN_IF(NS_FAILED(srv))) {
-    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
-    return promise.forget();
-  }
-
-  // Note: we only support U2F-style authentication for now, so we effectively
-  // require an AllowList.
-  if (!aOptions.mAllowList.WasPassed()) {
-    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
-    return promise.forget();
-  }
-
-  const Sequence<ScopedCredentialDescriptor>& allowList =
-    aOptions.mAllowList.Value();
-
-  // 4.1.2.6 Initialize issuedRequests to an empty list.
-  RefPtr<AssertionPromise> monitorPromise = requestMonitor->Ensure();
-
-  // 4.1.2.7 For each authenticator currently available on this platform,
-  // perform the following steps:
-  for(Authenticator u2ftoken : mAuthenticators) {
-    // 4.1.2.7.a If allowList is undefined or empty, let credentialList be an
-    // empty list. Otherwise, execute a platform-specific procedure to determine
-    // which, if any, credentials listed in allowList might be present on this
-    // authenticator, and set credentialList to this filtered list. If no such
-    // filtering is possible, set credentialList to an empty list.
-
-    nsTArray<CryptoBuffer> credentialList;
-
-    for (const ScopedCredentialDescriptor& scd : allowList) {
-      CryptoBuffer buf;
-      if (NS_WARN_IF(!buf.Assign(scd.mId))) {
-        continue;
-      }
-
-      // 4.1.2.7.b For each credential C within the credentialList that has a
-      // non- empty transports list, optionally use only the specified
-      // transports to get assertions using credential C.
-
-      // TODO: Filter using Transport
-      if (!credentialList.AppendElement(buf, mozilla::fallible)) {
-        requestMonitor->CancelNow();
-        promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
-        return promise.forget();
-      }
-    }
-
-    // 4.1.2.7.c If the above filtering process concludes that none of the
-    // credentials on allowList can possibly be on this authenticator, do not
-    // perform any of the following steps for this authenticator, and proceed to
-    // the next authenticator (if any).
-    if (credentialList.IsEmpty()) {
-      continue;
-    }
-
-    // 4.1.2.7.d Asynchronously invoke the authenticatorGetAssertion operation
-    // on this authenticator with rpIdHash, clientDataHash, credentialList, and
-    // clientExtensions as parameters.
-    U2FAuthGetAssertion(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
-                        clientDataHash, credentialList, aOptions.mExtensions);
-  }
-
-  requestMonitor->CompleteTask();
-
-  monitorPromise->Then(AbstractThread::MainThread(), __func__,
-    [promise] (AssertionPtr aAssertion) {
-      promise->MaybeResolve(aAssertion);
-    },
-    [promise] (nsresult aErrorCode) {
-      promise->MaybeReject(aErrorCode);
-  });
-
-  return promise.forget();
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/dom/u2f/WebAuthentication.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#ifndef mozilla_dom_WebAuthentication_h
-#define mozilla_dom_WebAuthentication_h
-
-#include "js/TypeDecls.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/dom/BindingDeclarations.h"
-#include "mozilla/dom/DOMException.h"
-#include "mozilla/dom/WebAuthenticationBinding.h"
-#include "mozilla/dom/WebCryptoCommon.h"
-#include "mozilla/ErrorResult.h"
-#include "mozilla/MozPromise.h"
-#include "mozilla/ReentrantMonitor.h"
-#include "mozilla/SharedThreadPool.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsWrapperCache.h"
-
-#include "U2FAuthenticator.h"
-#include "WebAuthnRequest.h"
-
-namespace mozilla {
-namespace dom {
-
-struct Account;
-class ArrayBufferViewOrArrayBuffer;
-struct AssertionOptions;
-class OwningArrayBufferViewOrArrayBuffer;
-struct ScopedCredentialOptions;
-struct ScopedCredentialParameters;
-
-} // namespace dom
-} // namespace mozilla
-
-namespace mozilla {
-namespace dom {
-
-typedef RefPtr<ScopedCredentialInfo> CredentialPtr;
-typedef RefPtr<WebAuthnAssertion> AssertionPtr;
-typedef WebAuthnRequest<CredentialPtr> CredentialRequest;
-typedef WebAuthnRequest<AssertionPtr> AssertionRequest;
-typedef MozPromise<CredentialPtr, nsresult, false> CredentialPromise;
-typedef MozPromise<AssertionPtr, nsresult, false> AssertionPromise;
-
-class WebAuthentication final : public nsISupports
-                              , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthentication)
-
-public:
-  explicit WebAuthentication(nsPIDOMWindowInner* aParent);
-
-protected:
-  ~WebAuthentication();
-
-public:
-  nsPIDOMWindowInner*
-  GetParentObject() const
-  {
-    return mParent;
-  }
-
-  virtual JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  already_AddRefed<Promise>
-  MakeCredential(JSContext* aCx, const Account& accountInformation,
-                 const Sequence<ScopedCredentialParameters>& cryptoParameters,
-                 const ArrayBufferViewOrArrayBuffer& attestationChallenge,
-                 const ScopedCredentialOptions& options);
-
-  already_AddRefed<Promise>
-  GetAssertion(const ArrayBufferViewOrArrayBuffer& assertionChallenge,
-               const AssertionOptions& options);
-
-private:
-  nsresult
-  InitLazily();
-
-  void
-  U2FAuthMakeCredential(const RefPtr<CredentialRequest>& aRequest,
-             const Authenticator& aToken, CryptoBuffer& aRpIdHash,
-             const nsACString& aClientData, CryptoBuffer& aClientDataHash,
-             const Account& aAccount,
-             const nsTArray<ScopedCredentialParameters>& aNormalizedParams,
-             const Optional<Sequence<ScopedCredentialDescriptor>>& aExcludeList,
-             const WebAuthnExtensions& aExtensions);
-  void
-  U2FAuthGetAssertion(const RefPtr<AssertionRequest>& aRequest,
-                   const Authenticator& aToken, CryptoBuffer& aRpIdHash,
-                   const nsACString& aClientData, CryptoBuffer& aClientDataHash,
-                   nsTArray<CryptoBuffer>& aAllowList,
-                   const WebAuthnExtensions& aExtensions);
-
-  nsresult
-  RelaxSameOrigin(const nsAString& aInputRpId, nsACString& aRelaxedRpId);
-
-  nsCOMPtr<nsPIDOMWindowInner> mParent;
-  nsString mOrigin;
-  Sequence<Authenticator> mAuthenticators;
-  bool mInitialized;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_WebAuthentication_h
deleted file mode 100644
--- a/dom/u2f/WebAuthnRequest.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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/. */
-
-#ifndef mozilla_dom_WebAuthnAsync_h
-#define mozilla_dom_WebAuthnAsync_h
-
-#include "mozilla/MozPromise.h"
-#include "mozilla/ReentrantMonitor.h"
-#include "mozilla/SharedThreadPool.h"
-#include "mozilla/TimeStamp.h"
-
-namespace mozilla {
-namespace dom {
-
-extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
-
-// WebAuthnRequest tracks the completion of a single WebAuthn request that
-// may run on multiple kinds of authenticators, and be subject to a deadline.
-template<class Success>
-class WebAuthnRequest {
-public:
-  WebAuthnRequest()
-    : mCancelled(false)
-    , mSuccess(false)
-    , mCountTokens(0)
-    , mTokensFailed(0)
-    , mReentrantMonitor("WebAuthnRequest")
-  {}
-
-  void AddActiveToken(const char* aCallSite)
-  {
-    MOZ_LOG(gWebauthLog, LogLevel::Debug,
-           ("WebAuthnRequest is tracking a new token, called from [%s]",
-            aCallSite));
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    MOZ_ASSERT(!IsComplete());
-    mCountTokens += 1;
-  }
-
-  bool IsComplete()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    return mCancelled || mSuccess ||
-      (mCountTokens > 0 && mTokensFailed == mCountTokens);
-  }
-
-  void CancelNow()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    // It's possible for a race to cause CancelNow to get called after
-    // a success or a cancel. We only complete once.
-    if (IsComplete()) {
-      return;
-    }
-
-    mCancelled = true;
-    mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
-  }
-
-  void SetFailure(nsresult aError)
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    // It's possible for a race to cause SetFailure to get called after
-    // a success or a cancel. We only complete once.
-    if (IsComplete()) {
-      return;
-    }
-
-    mTokensFailed += 1;
-    MOZ_ASSERT(mTokensFailed <= mCountTokens);
-
-    if (mTokensFailed == mCountTokens) {
-      // Provide the final error as being indicitive of the whole set.
-      mPromise.Reject(aError, __func__);
-    }
-  }
-
-  void SetSuccess(const Success& aResult)
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    // It's possible for a race to cause multiple calls to SetSuccess
-    // in succession. We will only select the earliest.
-    if (IsComplete()) {
-      return;
-    }
-
-    mSuccess = true;
-    mPromise.Resolve(aResult, __func__);
-  }
-
-  void SetDeadline(TimeDuration aDeadline)
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    MOZ_ASSERT(!IsComplete());
-    // TODO: Monitor the deadline and stop with a timeout error if it expires.
-  }
-
-  RefPtr<MozPromise<Success, nsresult, false>> Ensure()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-    MOZ_ASSERT(!IsComplete());
-    return mPromise.Ensure(__func__);
-  }
-
-  void CompleteTask()
-  {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-
-    if (mCountTokens == 0) {
-      // Special case for there being no tasks to complete
-      mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
-    }
-  }
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebAuthnRequest)
-
-private:
-  ~WebAuthnRequest() {};
-
-  bool mCancelled;
-  bool mSuccess;
-  int mCountTokens;
-  int mTokensFailed;
-  ReentrantMonitor mReentrantMonitor;
-  MozPromiseHolder<MozPromise<Success, nsresult, false>> mPromise;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_WebAuthnAsync_h
--- a/dom/u2f/moz.build
+++ b/dom/u2f/moz.build
@@ -1,36 +1,21 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS.mozilla.dom += [
-    'NSSU2FTokenRemote.h',
-    'ScopedCredential.h',
-    'ScopedCredentialInfo.h',
     'U2F.h',
     'U2FAuthenticator.h',
-    'USBToken.h',
-    'WebAuthentication.h',
-    'WebAuthnAssertion.h',
-    'WebAuthnAttestation.h',
-    'WebAuthnRequest.h',
 ]
 
 UNIFIED_SOURCES += [
-    'NSSU2FTokenRemote.cpp',
-    'ScopedCredential.cpp',
-    'ScopedCredentialInfo.cpp',
     'U2F.cpp',
-    'USBToken.cpp',
-    'WebAuthentication.cpp',
-    'WebAuthnAssertion.cpp',
-    'WebAuthnAttestation.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/dom/base',
--- a/dom/u2f/tests/mochitest.ini
+++ b/dom/u2f/tests/mochitest.ini
@@ -22,23 +22,8 @@ skip-if = !e10s
 [test_register_sign.html]
 skip-if = !e10s
 [test_appid_facet.html]
 skip-if = !e10s
 [test_appid_facet_insecure.html]
 skip-if = !e10s
 [test_appid_facet_subdomain.html]
 skip-if = !e10s
-[test_webauthn_loopback.html]
-skip-if = !e10s
-scheme = https
-[test_webauthn_no_token.html]
-skip-if = !e10s
-scheme = https
-[test_webauthn_make_credential.html]
-skip-if = !e10s
-scheme = https
-[test_webauthn_get_assertion.html]
-skip-if = !e10s
-scheme = https
-[test_webauthn_sameorigin.html]
-skip-if = !e10s
-scheme = https
\ No newline at end of file
rename from dom/u2f/NSSU2FTokenRemote.cpp
rename to dom/webauthn/NSSU2FTokenRemote.cpp
rename from dom/u2f/NSSU2FTokenRemote.h
rename to dom/webauthn/NSSU2FTokenRemote.h
rename from dom/u2f/ScopedCredential.cpp
rename to dom/webauthn/ScopedCredential.cpp
rename from dom/u2f/ScopedCredential.h
rename to dom/webauthn/ScopedCredential.h
rename from dom/u2f/ScopedCredentialInfo.cpp
rename to dom/webauthn/ScopedCredentialInfo.cpp
rename from dom/u2f/ScopedCredentialInfo.h
rename to dom/webauthn/ScopedCredentialInfo.h
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/WebAuthentication.cpp
@@ -0,0 +1,1057 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#include "mozilla/dom/WebAuthentication.h"
+#include "mozilla/dom/WebAuthnAssertion.h"
+#include "mozilla/dom/WebAuthnAttestation.h"
+
+#include "mozilla/dom/Promise.h"
+#include "nsICryptoHash.h"
+#include "pkix/Input.h"
+#include "pkixutil.h"
+
+#define PREF_WEBAUTHN_SOFTTOKEN_ENABLED "security.webauth.webauthn_enable_softtoken"
+#define PREF_WEBAUTHN_USBTOKEN_ENABLED  "security.webauth.webauthn_enable_usbtoken"
+
+namespace mozilla {
+namespace dom {
+
+static mozilla::LazyLogModule gWebauthLog("webauthn");
+
+// Only needed for refcounted objects.
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebAuthentication, mParent)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebAuthentication)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebAuthentication)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebAuthentication)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+template<class OOS>
+static nsresult
+GetAlgorithmName(JSContext* aCx, const OOS& aAlgorithm,
+                 /* out */ nsString& aName)
+{
+  MOZ_ASSERT(aAlgorithm.IsString()); // TODO: remove assertion when we coerce.
+
+  if (aAlgorithm.IsString()) {
+    // If string, then treat as algorithm name
+    aName.Assign(aAlgorithm.GetAsString());
+  } else {
+    // TODO: Coerce to string and extract name. See WebCryptoTask.cpp
+  }
+
+  if (!NormalizeToken(aName, aName)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+HashCString(nsICryptoHash* aHashService, const nsACString& aIn,
+            /* out */ CryptoBuffer& aOut)
+{
+  MOZ_ASSERT(aHashService);
+
+  nsresult rv = aHashService->Init(nsICryptoHash::SHA256);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aHashService->Update(
+         reinterpret_cast<const uint8_t*>(aIn.BeginReading()),aIn.Length());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoCString fullHash;
+  // Passing false below means we will get a binary result rather than a
+  // base64-encoded string.
+  rv = aHashService->Finish(false, fullHash);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  aOut.Assign(fullHash);
+  return rv;
+}
+
+static nsresult
+AssembleClientData(const nsAString& aOrigin, const CryptoBuffer& aChallenge,
+                   /* out */ nsACString& aJsonOut)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsString challengeBase64;
+  nsresult rv = aChallenge.ToJwkBase64(challengeBase64);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  WebAuthnClientData clientDataObject;
+  clientDataObject.mOrigin.Assign(aOrigin);
+  clientDataObject.mHashAlg.SetAsString().Assign(NS_LITERAL_STRING("S256"));
+  clientDataObject.mChallenge.Assign(challengeBase64);
+
+  nsAutoString temp;
+  if (NS_WARN_IF(!clientDataObject.ToJSON(temp))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aJsonOut.Assign(NS_ConvertUTF16toUTF8(temp));
+  return NS_OK;
+}
+
+static nsresult
+ScopedCredentialGetData(const ScopedCredentialDescriptor& aSCD,
+                        /* out */ uint8_t** aBuf, /* out */ uint32_t* aBufLen)
+{
+  MOZ_ASSERT(aBuf);
+  MOZ_ASSERT(aBufLen);
+
+  if (aSCD.mId.IsArrayBufferView()) {
+    const ArrayBufferView& view = aSCD.mId.GetAsArrayBufferView();
+    view.ComputeLengthAndData();
+    *aBuf = view.Data();
+    *aBufLen = view.Length();
+  } else if (aSCD.mId.IsArrayBuffer()) {
+    const ArrayBuffer& buffer = aSCD.mId.GetAsArrayBuffer();
+    buffer.ComputeLengthAndData();
+    *aBuf = buffer.Data();
+    *aBufLen = buffer.Length();
+  } else {
+    MOZ_ASSERT(false);
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+ReadToCryptoBuffer(pkix::Reader& aSrc, /* out */ CryptoBuffer& aDest,
+                   uint32_t aLen)
+{
+  if (aSrc.EnsureLength(aLen) != pkix::Success) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+
+  aDest.ClearAndRetainStorage();
+
+  for (uint32_t offset = 0; offset < aLen; ++offset) {
+    uint8_t b;
+    if (aSrc.Read(b) != pkix::Success) {
+      return NS_ERROR_DOM_UNKNOWN_ERR;
+    }
+    if (!aDest.AppendElement(b, mozilla::fallible)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+U2FAssembleAuthenticatorData(/* out */ CryptoBuffer& aAuthenticatorData,
+                             const CryptoBuffer& aRpIdHash,
+                             const CryptoBuffer& aSignatureData)
+{
+  // The AuthenticatorData for U2F devices is the concatenation of the
+  // RP ID with the output of the U2F Sign operation.
+  if (aRpIdHash.Length() != 32) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (!aAuthenticatorData.AppendElements(aRpIdHash, mozilla::fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!aAuthenticatorData.AppendElements(aSignatureData, mozilla::fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  return NS_OK;
+}
+
+static nsresult
+U2FDecomposeRegistrationResponse(const CryptoBuffer& aResponse,
+                                 /* out */ CryptoBuffer& aPubKeyBuf,
+                                 /* out */ CryptoBuffer& aKeyHandleBuf,
+                                 /* out */ CryptoBuffer& aAttestationCertBuf,
+                                 /* out */ CryptoBuffer& aSignatureBuf)
+{
+  // U2F v1.1 Format via
+  // http://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html
+  //
+  // Bytes  Value
+  // 1      0x05
+  // 65     public key
+  // 1      key handle length
+  // *      key handle
+  // ASN.1  attestation certificate
+  // *      attestation signature
+
+  pkix::Input u2fResponse;
+  u2fResponse.Init(aResponse.Elements(), aResponse.Length());
+
+  pkix::Reader input(u2fResponse);
+
+  uint8_t b;
+  if (input.Read(b) != pkix::Success) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+  if (b != 0x05) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+
+  nsresult rv = ReadToCryptoBuffer(input, aPubKeyBuf, 65);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  uint8_t handleLen;
+  if (input.Read(handleLen) != pkix::Success) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+
+  rv = ReadToCryptoBuffer(input, aKeyHandleBuf, handleLen);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // We have to parse the ASN.1 SEQUENCE on the outside to determine the cert's
+  // length.
+  pkix::Input cert;
+  if (pkix::der::ExpectTagAndGetValue(input, pkix::der::SEQUENCE, cert)
+        != pkix::Success) {
+    return NS_ERROR_DOM_UNKNOWN_ERR;
+  }
+
+  pkix::Reader certInput(cert);
+  rv = ReadToCryptoBuffer(certInput, aAttestationCertBuf, cert.GetLength());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // The remainder of u2fResponse is the signature
+  pkix::Input u2fSig;
+  input.SkipToEnd(u2fSig);
+  pkix::Reader sigInput(u2fSig);
+  rv = ReadToCryptoBuffer(sigInput, aSignatureBuf, u2fSig.GetLength());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+WebAuthentication::WebAuthentication(nsPIDOMWindowInner* aParent)
+  : mInitialized(false)
+{
+  mParent = do_QueryInterface(aParent);
+  MOZ_ASSERT(mParent);
+}
+
+WebAuthentication::~WebAuthentication()
+{}
+
+nsresult
+WebAuthentication::InitLazily()
+{
+  if (mInitialized) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mParent);
+  if (!mParent) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIDocument> doc = mParent->GetDoc();
+  MOZ_ASSERT(doc);
+
+  nsIPrincipal* principal = doc->NodePrincipal();
+  nsresult rv = nsContentUtils::GetUTFOrigin(principal, mOrigin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (NS_WARN_IF(mOrigin.IsEmpty())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // This only functions in e10s mode
+  // TODO: Remove in Bug 1323339
+  if (XRE_IsParentProcess()) {
+    MOZ_LOG(gWebauthLog, LogLevel::Debug,
+            ("Is non-e10s Process, WebAuthn not available"));
+    return NS_ERROR_FAILURE;
+  }
+
+  if (Preferences::GetBool(PREF_WEBAUTHN_SOFTTOKEN_ENABLED)) {
+    if (!mAuthenticators.AppendElement(new NSSU2FTokenRemote(),
+                                       mozilla::fallible)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  mInitialized = true;
+  return NS_OK;
+}
+
+JSObject*
+WebAuthentication::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return WebAuthenticationBinding::Wrap(aCx, this, aGivenProto);
+}
+
+// NOTE: This method represents a theoretical way to use a U2F-compliant token
+// to produce the result of the WebAuthn MakeCredential method. The exact
+// mapping of U2F data fields to WebAuthn data fields is still a matter of
+// ongoing discussion, and this should not be taken as anything but a point-in-
+// time possibility.
+void
+WebAuthentication::U2FAuthMakeCredential(
+             const RefPtr<CredentialRequest>& aRequest,
+             const Authenticator& aToken, CryptoBuffer& aRpIdHash,
+             const nsACString& aClientData, CryptoBuffer& aClientDataHash,
+             const Account& aAccount,
+             const nsTArray<ScopedCredentialParameters>& aNormalizedParams,
+             const Optional<Sequence<ScopedCredentialDescriptor>>& aExcludeList,
+             const WebAuthnExtensions& aExtensions)
+{
+  MOZ_LOG(gWebauthLog, LogLevel::Debug, ("U2FAuthMakeCredential"));
+  aRequest->AddActiveToken(__func__);
+
+  // 5.1.1 When this operation is invoked, the authenticator must perform the
+  // following procedure:
+
+  // 5.1.1.a Check if all the supplied parameters are syntactically well-
+  // formed and of the correct length. If not, return an error code equivalent
+  // to UnknownError and terminate the operation.
+
+  if ((aRpIdHash.Length() != SHA256_LENGTH) ||
+      (aClientDataHash.Length() != SHA256_LENGTH)) {
+    aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
+    return;
+  }
+
+  // 5.1.1.b Check if at least one of the specified combinations of
+  // ScopedCredentialType and cryptographic parameters is supported. If not,
+  // return an error code equivalent to NotSupportedError and terminate the
+  // operation.
+
+  bool isValidCombination = false;
+
+  for (size_t a = 0; a < aNormalizedParams.Length(); ++a) {
+    if (aNormalizedParams[a].mType == ScopedCredentialType::ScopedCred &&
+        aNormalizedParams[a].mAlgorithm.IsString() &&
+        aNormalizedParams[a].mAlgorithm.GetAsString().EqualsLiteral(
+          WEBCRYPTO_NAMED_CURVE_P256)) {
+      isValidCombination = true;
+      break;
+    }
+  }
+  if (!isValidCombination) {
+    aRequest->SetFailure(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
+  }
+
+  // 5.1.1.c Check if a credential matching any of the supplied
+  // ScopedCredential identifiers is present on this authenticator. If so,
+  // return an error code equivalent to NotAllowedError and terminate the
+  // operation.
+
+  if (aExcludeList.WasPassed()) {
+    const Sequence<ScopedCredentialDescriptor>& list = aExcludeList.Value();
+
+    for (const ScopedCredentialDescriptor& scd : list) {
+      bool isRegistered = false;
+
+      uint8_t *data;
+      uint32_t len;
+
+      // data is owned by the Descriptor, do don't free it here.
+      if (NS_FAILED(ScopedCredentialGetData(scd, &data, &len))) {
+        aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
+        return;
+      }
+
+      nsresult rv = aToken->IsRegistered(data, len, &isRegistered);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        aRequest->SetFailure(rv);
+        return;
+      }
+
+      if (isRegistered) {
+        aRequest->SetFailure(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+        return;
+      }
+    }
+  }
+
+  // 5.1.1.d Prompt the user for consent to create a new credential. The
+  // prompt for obtaining this consent is shown by the authenticator if it has
+  // its own output capability, or by the user agent otherwise. If the user
+  // denies consent, return an error code equivalent to NotAllowedError and
+  // terminate the operation.
+
+  // 5.1.1.d Once user consent has been obtained, generate a new credential
+  // object
+
+  // 5.1.1.e If any error occurred while creating the new credential object,
+  // return an error code equivalent to UnknownError and terminate the
+  // operation.
+
+  // 5.1.1.f Process all the supported extensions requested by the client, and
+  // generate an attestation statement. If no authority key is available to
+  // sign such an attestation statement, then the authenticator performs self
+  // attestation of the credential with its own private key. For more details
+  // on attestation, see §5.3 Credential Attestation Statements.
+
+  // No extensions are supported
+
+  // 4.1.1.11 While issuedRequests is not empty, perform the following actions
+  // depending upon the adjustedTimeout timer and responses from the
+  // authenticators:
+
+  // 4.1.1.11.a If the adjustedTimeout timer expires, then for each entry in
+  // issuedRequests invoke the authenticatorCancel operation on that
+  // authenticator and remove its entry from the list.
+
+  uint8_t* buffer;
+  uint32_t bufferlen;
+
+  nsresult rv = aToken->Register(aRpIdHash.Elements(), aRpIdHash.Length(),
+                                 aClientDataHash.Elements(),
+                                 aClientDataHash.Length(), &buffer, &bufferlen);
+
+  // 4.1.1.11.b If any authenticator returns a status indicating that the user
+  // cancelled the operation, delete that authenticator’s entry from
+  // issuedRequests. For each remaining entry in issuedRequests invoke the
+  // authenticatorCancel operation on that authenticator and remove its entry
+  // from the list.
+
+  // 4.1.1.11.c If any authenticator returns an error status, delete the
+  // corresponding entry from issuedRequests.
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRequest->SetFailure(NS_ERROR_DOM_UNKNOWN_ERR);
+    return;
+  }
+
+  MOZ_ASSERT(buffer);
+  CryptoBuffer regData;
+  if (NS_WARN_IF(!regData.Assign(buffer, bufferlen))) {
+    free(buffer);
+    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+  free(buffer);
+
+  // Decompose the U2F registration packet
+  CryptoBuffer pubKeyBuf;
+  CryptoBuffer keyHandleBuf;
+  CryptoBuffer attestationCertBuf;
+  CryptoBuffer signatureBuf;
+
+  rv = U2FDecomposeRegistrationResponse(regData, pubKeyBuf, keyHandleBuf,
+                                        attestationCertBuf, signatureBuf);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRequest->SetFailure(rv);
+    return;
+  }
+
+  // Sign the aClientDataHash explicitly to get the format needed for
+  // the AuthenticatorData parameter of WebAuthnAttestation. This might
+  // be temporary while the spec settles down how to incorporate U2F.
+  rv = aToken->Sign(aRpIdHash.Elements(), aRpIdHash.Length(),
+                    aClientDataHash.Elements(), aClientDataHash.Length(),
+                    keyHandleBuf.Elements(), keyHandleBuf.Length(), &buffer,
+                    &bufferlen);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRequest->SetFailure(rv);
+    return;
+  }
+
+  MOZ_ASSERT(buffer);
+  CryptoBuffer signatureData;
+  if (NS_WARN_IF(!signatureData.Assign(buffer, bufferlen))) {
+    free(buffer);
+    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+  free(buffer);
+
+  CryptoBuffer clientDataBuf;
+  if (!clientDataBuf.Assign(aClientData)) {
+    aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+
+  CryptoBuffer authenticatorDataBuf;
+  rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, aRpIdHash,
+                                    signatureData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRequest->SetFailure(rv);
+    return;
+  }
+
+  // 4.1.1.11.d If any authenticator indicates success:
+
+  // 4.1.1.11.d.1 Remove this authenticator’s entry from issuedRequests.
+
+  // 4.1.1.11.d.2 Create a new ScopedCredentialInfo object named value and
+  // populate its fields with the values returned from the authenticator as well
+  // as the clientDataJSON computed earlier.
+
+  RefPtr<ScopedCredential> credential = new ScopedCredential(this);
+  credential->SetType(ScopedCredentialType::ScopedCred);
+  credential->SetId(keyHandleBuf);
+
+  RefPtr<WebAuthnAttestation> attestation = new WebAuthnAttestation(this);
+  attestation->SetFormat(NS_LITERAL_STRING("u2f"));
+  attestation->SetClientData(clientDataBuf);
+  attestation->SetAuthenticatorData(authenticatorDataBuf);
+  attestation->SetAttestation(regData);
+
+  CredentialPtr info = new ScopedCredentialInfo(this);
+  info->SetCredential(credential);
+  info->SetAttestation(attestation);
+
+  // 4.1.1.11.d.3 For each remaining entry in issuedRequests invoke the
+  // authenticatorCancel operation on that authenticator and remove its entry
+  // from the list.
+
+  // 4.1.1.11.d.4 Resolve promise with value and terminate this algorithm.
+  aRequest->SetSuccess(info);
+}
+
+// NOTE: This method represents a theoretical way to use a U2F-compliant token
+// to produce the result of the WebAuthn GetAssertion method. The exact mapping
+// of U2F data fields to WebAuthn data fields is still a matter of ongoing
+// discussion, and this should not be taken as anything but a point-in- time
+// possibility.
+void
+WebAuthentication::U2FAuthGetAssertion(const RefPtr<AssertionRequest>& aRequest,
+                    const Authenticator& aToken, CryptoBuffer& aRpIdHash,
+                    const nsACString& aClientData, CryptoBuffer& aClientDataHash,
+                    nsTArray<CryptoBuffer>& aAllowList,
+                    const WebAuthnExtensions& aExtensions)
+{
+  MOZ_LOG(gWebauthLog, LogLevel::Debug, ("U2FAuthGetAssertion"));
+
+  // 4.1.2.7.e Add an entry to issuedRequests, corresponding to this request.
+  aRequest->AddActiveToken(__func__);
+
+  // 4.1.2.8 While issuedRequests is not empty, perform the following actions
+  // depending upon the adjustedTimeout timer and responses from the
+  // authenticators:
+
+  // 4.1.2.8.a If the timer for adjustedTimeout expires, then for each entry
+  // in issuedRequests invoke the authenticatorCancel operation on that
+  // authenticator and remove its entry from the list.
+
+  for (CryptoBuffer& allowedCredential : aAllowList) {
+    bool isRegistered = false;
+    nsresult rv = aToken->IsRegistered(allowedCredential.Elements(),
+                                       allowedCredential.Length(),
+                                       &isRegistered);
+
+    // 4.1.2.8.b If any authenticator returns a status indicating that the user
+    // cancelled the operation, delete that authenticator’s entry from
+    // issuedRequests. For each remaining entry in issuedRequests invoke the
+    // authenticatorCancel operation on that authenticator, and remove its entry
+    // from the list.
+
+    // 4.1.2.8.c If any authenticator returns an error status, delete the
+    // corresponding entry from issuedRequests.
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRequest->SetFailure(rv);
+      return;
+    }
+
+    if (!isRegistered) {
+      continue;
+    }
+
+    // Sign
+    uint8_t* buffer;
+    uint32_t bufferlen;
+    rv = aToken->Sign(aRpIdHash.Elements(), aRpIdHash.Length(),
+                      aClientDataHash.Elements(), aClientDataHash.Length(),
+                      allowedCredential.Elements(), allowedCredential.Length(),
+                      &buffer, &bufferlen);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRequest->SetFailure(rv);
+      return;
+    }
+
+    MOZ_ASSERT(buffer);
+    CryptoBuffer signatureData;
+    if (NS_WARN_IF(!signatureData.Assign(buffer, bufferlen))) {
+      free(buffer);
+      aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+    free(buffer);
+
+    // 4.1.2.8.d If any authenticator returns success:
+
+    // 4.1.2.8.d.1 Remove this authenticator’s entry from issuedRequests.
+
+    // 4.1.2.8.d.2 Create a new WebAuthnAssertion object named value and
+    // populate its fields with the values returned from the authenticator as
+    // well as the clientDataJSON computed earlier.
+
+    CryptoBuffer clientDataBuf;
+    if (!clientDataBuf.Assign(aClientData)) {
+      aRequest->SetFailure(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+
+    CryptoBuffer authenticatorDataBuf;
+    rv = U2FAssembleAuthenticatorData(authenticatorDataBuf, aRpIdHash,
+                                      signatureData);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRequest->SetFailure(rv);
+      return;
+    }
+
+    RefPtr<ScopedCredential> credential = new ScopedCredential(this);
+    credential->SetType(ScopedCredentialType::ScopedCred);
+    credential->SetId(allowedCredential);
+
+    AssertionPtr assertion = new WebAuthnAssertion(this);
+    assertion->SetCredential(credential);
+    assertion->SetClientData(clientDataBuf);
+    assertion->SetAuthenticatorData(authenticatorDataBuf);
+    assertion->SetSignature(signatureData);
+
+    // 4.1.2.8.d.3 For each remaining entry in issuedRequests invoke the
+    // authenticatorCancel operation on that authenticator and remove its entry
+    // from the list.
+
+    // 4.1.2.8.d.4 Resolve promise with value and terminate this algorithm.
+    aRequest->SetSuccess(assertion);
+    return;
+  }
+
+  // 4.1.2.9 Reject promise with a DOMException whose name is "NotAllowedError",
+  // and terminate this algorithm.
+  aRequest->SetFailure(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+}
+
+nsresult
+WebAuthentication::RelaxSameOrigin(const nsAString& aInputRpId,
+                                   /* out */ nsACString& aRelaxedRpId)
+{
+  MOZ_ASSERT(mParent);
+  nsCOMPtr<nsIDocument> document = mParent->GetDoc();
+  if (!document || !document->IsHTMLDocument()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // TODO: Bug 1329764: Invoke the Relax Algorithm, once properly defined
+  aRelaxedRpId.Assign(NS_ConvertUTF16toUTF8(aInputRpId));
+  return NS_OK;
+}
+
+already_AddRefed<Promise>
+WebAuthentication::MakeCredential(JSContext* aCx, const Account& aAccount,
+                  const Sequence<ScopedCredentialParameters>& aCryptoParameters,
+                  const ArrayBufferViewOrArrayBuffer& aChallenge,
+                  const ScopedCredentialOptions& aOptions)
+{
+  MOZ_ASSERT(mParent);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+  if (!global) {
+    return nullptr;
+  }
+
+  ErrorResult rv;
+  RefPtr<Promise> promise = Promise::Create(global, rv);
+
+  nsresult initRv = InitLazily();
+  if (NS_FAILED(initRv)) {
+    promise->MaybeReject(initRv);
+    return promise.forget();
+  }
+
+  // 4.1.1.1 If timeoutSeconds was specified, check if its value lies within a
+  // reasonable range as defined by the platform and if not, correct it to the
+  // closest value lying within that range.
+
+  double adjustedTimeout = 30.0;
+  if (aOptions.mTimeoutSeconds.WasPassed()) {
+    adjustedTimeout = aOptions.mTimeoutSeconds.Value();
+    adjustedTimeout = std::max(15.0, adjustedTimeout);
+    adjustedTimeout = std::min(120.0, adjustedTimeout);
+  }
+
+  // 4.1.1.2 Let promise be a new Promise. Return promise and start a timer for
+  // adjustedTimeout seconds.
+
+  RefPtr<CredentialRequest> requestMonitor = new CredentialRequest();
+  requestMonitor->SetDeadline(TimeDuration::FromSeconds(adjustedTimeout));
+
+  if (mOrigin.EqualsLiteral("null")) {
+    // 4.1.1.3 If callerOrigin is an opaque origin, reject promise with a
+    // DOMException whose name is "NotAllowedError", and terminate this
+    // algorithm
+    MOZ_LOG(gWebauthLog, LogLevel::Debug, ("Rejecting due to opaque origin"));
+    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+    return promise.forget();
+  }
+
+  nsCString rpId;
+  if (!aOptions.mRpId.WasPassed()) {
+    // 4.1.1.3.a If rpId is not specified, then set rpId to callerOrigin, and
+    // rpIdHash to the SHA-256 hash of rpId.
+    rpId.Assign(NS_ConvertUTF16toUTF8(mOrigin));
+  } else {
+    // 4.1.1.3.b If rpId is specified, then invoke the procedure used for
+    // relaxing the same-origin restriction by setting the document.domain
+    // attribute, using rpId as the given value but without changing the current
+    // document’s domain. If no errors are thrown, set rpId to the value of host
+    // as computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
+    // Otherwise, reject promise with a DOMException whose name is
+    // "SecurityError", and terminate this algorithm.
+
+    if (NS_FAILED(RelaxSameOrigin(aOptions.mRpId.Value(), rpId))) {
+      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+      return promise.forget();
+    }
+  }
+
+  CryptoBuffer rpIdHash;
+  if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
+    promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+    return promise.forget();
+  }
+
+  nsresult srv;
+  nsCOMPtr<nsICryptoHash> hashService =
+    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  srv = HashCString(hashService, rpId, rpIdHash);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  // 4.1.1.4 Process each element of cryptoParameters using the following steps,
+  // to produce a new sequence normalizedParameters.
+  nsTArray<ScopedCredentialParameters> normalizedParams;
+  for (size_t a = 0; a < aCryptoParameters.Length(); ++a) {
+    // 4.1.1.4.a Let current be the currently selected element of
+    // cryptoParameters.
+
+    // 4.1.1.4.b If current.type does not contain a ScopedCredentialType
+    // supported by this implementation, then stop processing current and move
+    // on to the next element in cryptoParameters.
+    if (aCryptoParameters[a].mType != ScopedCredentialType::ScopedCred) {
+      continue;
+    }
+
+    // 4.1.1.4.c Let normalizedAlgorithm be the result of normalizing an
+    // algorithm using the procedure defined in [WebCryptoAPI], with alg set to
+    // current.algorithm and op set to 'generateKey'. If an error occurs during
+    // this procedure, then stop processing current and move on to the next
+    // element in cryptoParameters.
+
+    nsString algName;
+    if (NS_FAILED(GetAlgorithmName(aCx, aCryptoParameters[a].mAlgorithm,
+                                   algName))) {
+      continue;
+    }
+
+    // 4.1.1.4.d Add a new object of type ScopedCredentialParameters to
+    // normalizedParameters, with type set to current.type and algorithm set to
+    // normalizedAlgorithm.
+    ScopedCredentialParameters normalizedObj;
+    normalizedObj.mType = aCryptoParameters[a].mType;
+    normalizedObj.mAlgorithm.SetAsString().Assign(algName);
+
+    if (!normalizedParams.AppendElement(normalizedObj, mozilla::fallible)){
+      promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+      return promise.forget();
+    }
+  }
+
+  // 4.1.1.5 If normalizedAlgorithm is empty and cryptoParameters was not empty,
+  // cancel the timer started in step 2, reject promise with a DOMException
+  // whose name is "NotSupportedError", and terminate this algorithm.
+  if (normalizedParams.IsEmpty() && !aCryptoParameters.IsEmpty()) {
+    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return promise.forget();
+  }
+
+  // 4.1.1.6 If excludeList is undefined, set it to the empty list.
+
+  // 4.1.1.7 If extensions was specified, process any extensions supported by
+  // this client platform, to produce the extension data that needs to be sent
+  // to the authenticator. If an error is encountered while processing an
+  // extension, skip that extension and do not produce any extension data for
+  // it. Call the result of this processing clientExtensions.
+
+  // Currently no extensions are supported
+
+  // 4.1.1.8 Use attestationChallenge, callerOrigin and rpId, along with the
+  // token binding key associated with callerOrigin (if any), to create a
+  // ClientData structure representing this request. Choose a hash algorithm for
+  // hashAlg and compute the clientDataJSON and clientDataHash.
+
+  CryptoBuffer challenge;
+  if (!challenge.Assign(aChallenge)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  nsAutoCString clientDataJSON;
+  srv = AssembleClientData(mOrigin, challenge, clientDataJSON);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  CryptoBuffer clientDataHash;
+  if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  srv = HashCString(hashService, clientDataJSON, clientDataHash);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  // 4.1.1.9 Initialize issuedRequests to an empty list.
+  RefPtr<CredentialPromise> monitorPromise = requestMonitor->Ensure();
+
+  // 4.1.1.10 For each authenticator currently available on this platform:
+  // asynchronously invoke the authenticatorMakeCredential operation on that
+  // authenticator with rpIdHash, clientDataHash, accountInformation,
+  // normalizedParameters, excludeList and clientExtensions as parameters. Add a
+  // corresponding entry to issuedRequests.
+  for (Authenticator u2ftoken : mAuthenticators) {
+    // 4.1.1.10.a For each credential C in excludeList that has a non-empty
+    // transports list, optionally use only the specified transports to test for
+    // the existence of C.
+    U2FAuthMakeCredential(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
+                          clientDataHash, aAccount, normalizedParams,
+                          aOptions.mExcludeList, aOptions.mExtensions);
+  }
+
+  requestMonitor->CompleteTask();
+
+  monitorPromise->Then(AbstractThread::MainThread(), __func__,
+    [promise] (CredentialPtr aInfo) {
+      promise->MaybeResolve(aInfo);
+    },
+    [promise] (nsresult aErrorCode) {
+      promise->MaybeReject(aErrorCode);
+  });
+
+  return promise.forget();
+}
+
+already_AddRefed<Promise>
+WebAuthentication::GetAssertion(const ArrayBufferViewOrArrayBuffer& aChallenge,
+                                const AssertionOptions& aOptions)
+{
+  MOZ_ASSERT(mParent);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+  if (!global) {
+    return nullptr;
+  }
+
+  // 4.1.2.1 If timeoutSeconds was specified, check if its value lies within a
+  // reasonable range as defined by the platform and if not, correct it to the
+  // closest value lying within that range.
+
+  double adjustedTimeout = 30.0;
+  if (aOptions.mTimeoutSeconds.WasPassed()) {
+    adjustedTimeout = aOptions.mTimeoutSeconds.Value();
+    adjustedTimeout = std::max(15.0, adjustedTimeout);
+    adjustedTimeout = std::min(120.0, adjustedTimeout);
+  }
+
+  // 4.1.2.2 Let promise be a new Promise. Return promise and start a timer for
+  // adjustedTimeout seconds.
+
+  RefPtr<AssertionRequest> requestMonitor = new AssertionRequest();
+  requestMonitor->SetDeadline(TimeDuration::FromSeconds(adjustedTimeout));
+
+  ErrorResult rv;
+  RefPtr<Promise> promise = Promise::Create(global, rv);
+
+  nsresult initRv = InitLazily();
+  if (NS_FAILED(initRv)) {
+    promise->MaybeReject(initRv);
+    return promise.forget();
+  }
+
+  if (mOrigin.EqualsLiteral("null")) {
+    // 4.1.2.3 If callerOrigin is an opaque origin, reject promise with a
+    // DOMException whose name is "NotAllowedError", and terminate this algorithm
+    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+    return promise.forget();
+  }
+
+  nsCString rpId;
+  if (!aOptions.mRpId.WasPassed()) {
+    // 4.1.2.3.a If rpId is not specified, then set rpId to callerOrigin, and
+    // rpIdHash to the SHA-256 hash of rpId.
+    rpId.Assign(NS_ConvertUTF16toUTF8(mOrigin));
+  } else {
+    // 4.1.2.3.b If rpId is specified, then invoke the procedure used for
+    // relaxing the same-origin restriction by setting the document.domain
+    // attribute, using rpId as the given value but without changing the current
+    // document’s domain. If no errors are thrown, set rpId to the value of host
+    // as computed by this procedure, and rpIdHash to the SHA-256 hash of rpId.
+    // Otherwise, reject promise with a DOMException whose name is
+    // "SecurityError", and terminate this algorithm.
+
+    if (NS_FAILED(RelaxSameOrigin(aOptions.mRpId.Value(), rpId))) {
+      promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+      return promise.forget();
+    }
+  }
+
+  CryptoBuffer rpIdHash;
+  if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  nsresult srv;
+  nsCOMPtr<nsICryptoHash> hashService =
+    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  srv = HashCString(hashService, rpId, rpIdHash);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  // 4.1.2.4 If extensions was specified, process any extensions supported by
+  // this client platform, to produce the extension data that needs to be sent
+  // to the authenticator. If an error is encountered while processing an
+  // extension, skip that extension and do not produce any extension data for
+  // it. Call the result of this processing clientExtensions.
+
+  // TODO
+
+  // 4.1.2.5 Use assertionChallenge, callerOrigin and rpId, along with the token
+  // binding key associated with callerOrigin (if any), to create a ClientData
+  // structure representing this request. Choose a hash algorithm for hashAlg
+  // and compute the clientDataJSON and clientDataHash.
+  CryptoBuffer challenge;
+  if (!challenge.Assign(aChallenge)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  nsAutoCString clientDataJSON;
+  srv = AssembleClientData(mOrigin, challenge, clientDataJSON);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  CryptoBuffer clientDataHash;
+  if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  srv = HashCString(hashService, clientDataJSON, clientDataHash);
+  if (NS_WARN_IF(NS_FAILED(srv))) {
+    promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
+    return promise.forget();
+  }
+
+  // Note: we only support U2F-style authentication for now, so we effectively
+  // require an AllowList.
+  if (!aOptions.mAllowList.WasPassed()) {
+    promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+    return promise.forget();
+  }
+
+  const Sequence<ScopedCredentialDescriptor>& allowList =
+    aOptions.mAllowList.Value();
+
+  // 4.1.2.6 Initialize issuedRequests to an empty list.
+  RefPtr<AssertionPromise> monitorPromise = requestMonitor->Ensure();
+
+  // 4.1.2.7 For each authenticator currently available on this platform,
+  // perform the following steps:
+  for(Authenticator u2ftoken : mAuthenticators) {
+    // 4.1.2.7.a If allowList is undefined or empty, let credentialList be an
+    // empty list. Otherwise, execute a platform-specific procedure to determine
+    // which, if any, credentials listed in allowList might be present on this
+    // authenticator, and set credentialList to this filtered list. If no such
+    // filtering is possible, set credentialList to an empty list.
+
+    nsTArray<CryptoBuffer> credentialList;
+
+    for (const ScopedCredentialDescriptor& scd : allowList) {
+      CryptoBuffer buf;
+      if (NS_WARN_IF(!buf.Assign(scd.mId))) {
+        continue;
+      }
+
+      // 4.1.2.7.b For each credential C within the credentialList that has a
+      // non- empty transports list, optionally use only the specified
+      // transports to get assertions using credential C.
+
+      // TODO: Filter using Transport
+      if (!credentialList.AppendElement(buf, mozilla::fallible)) {
+        requestMonitor->CancelNow();
+        promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
+        return promise.forget();
+      }
+    }
+
+    // 4.1.2.7.c If the above filtering process concludes that none of the
+    // credentials on allowList can possibly be on this authenticator, do not
+    // perform any of the following steps for this authenticator, and proceed to
+    // the next authenticator (if any).
+    if (credentialList.IsEmpty()) {
+      continue;
+    }
+
+    // 4.1.2.7.d Asynchronously invoke the authenticatorGetAssertion operation
+    // on this authenticator with rpIdHash, clientDataHash, credentialList, and
+    // clientExtensions as parameters.
+    U2FAuthGetAssertion(requestMonitor, u2ftoken, rpIdHash, clientDataJSON,
+                        clientDataHash, credentialList, aOptions.mExtensions);
+  }
+
+  requestMonitor->CompleteTask();
+
+  monitorPromise->Then(AbstractThread::MainThread(), __func__,
+    [promise] (AssertionPtr aAssertion) {
+      promise->MaybeResolve(aAssertion);
+    },
+    [promise] (nsresult aErrorCode) {
+      promise->MaybeReject(aErrorCode);
+  });
+
+  return promise.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/WebAuthentication.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_dom_WebAuthentication_h
+#define mozilla_dom_WebAuthentication_h
+
+#include "hasht.h"
+#include "js/TypeDecls.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/WebAuthenticationBinding.h"
+#include "mozilla/dom/WebCryptoCommon.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/SharedThreadPool.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsNetCID.h"
+#include "nsWrapperCache.h"
+
+#include "U2FAuthenticator.h"
+#include "WebAuthnRequest.h"
+
+namespace mozilla {
+namespace dom {
+
+struct Account;
+class ArrayBufferViewOrArrayBuffer;
+struct AssertionOptions;
+class OwningArrayBufferViewOrArrayBuffer;
+struct ScopedCredentialOptions;
+struct ScopedCredentialParameters;
+
+} // namespace dom
+} // namespace mozilla
+
+namespace mozilla {
+namespace dom {
+
+typedef RefPtr<ScopedCredentialInfo> CredentialPtr;
+typedef RefPtr<WebAuthnAssertion> AssertionPtr;
+typedef WebAuthnRequest<CredentialPtr> CredentialRequest;
+typedef WebAuthnRequest<AssertionPtr> AssertionRequest;
+typedef MozPromise<CredentialPtr, nsresult, false> CredentialPromise;
+typedef MozPromise<AssertionPtr, nsresult, false> AssertionPromise;
+
+class WebAuthentication final : public nsISupports
+                              , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebAuthentication)
+
+public:
+  explicit WebAuthentication(nsPIDOMWindowInner* aParent);
+
+protected:
+  ~WebAuthentication();
+
+public:
+  nsPIDOMWindowInner*
+  GetParentObject() const
+  {
+    return mParent;
+  }
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  already_AddRefed<Promise>
+  MakeCredential(JSContext* aCx, const Account& accountInformation,
+                 const Sequence<ScopedCredentialParameters>& cryptoParameters,
+                 const ArrayBufferViewOrArrayBuffer& attestationChallenge,
+                 const ScopedCredentialOptions& options);
+
+  already_AddRefed<Promise>
+  GetAssertion(const ArrayBufferViewOrArrayBuffer& assertionChallenge,
+               const AssertionOptions& options);
+
+private:
+  nsresult
+  InitLazily();
+
+  void
+  U2FAuthMakeCredential(const RefPtr<CredentialRequest>& aRequest,
+             const Authenticator& aToken, CryptoBuffer& aRpIdHash,
+             const nsACString& aClientData, CryptoBuffer& aClientDataHash,
+             const Account& aAccount,
+             const nsTArray<ScopedCredentialParameters>& aNormalizedParams,
+             const Optional<Sequence<ScopedCredentialDescriptor>>& aExcludeList,
+             const WebAuthnExtensions& aExtensions);
+  void
+  U2FAuthGetAssertion(const RefPtr<AssertionRequest>& aRequest,
+                   const Authenticator& aToken, CryptoBuffer& aRpIdHash,
+                   const nsACString& aClientData, CryptoBuffer& aClientDataHash,
+                   nsTArray<CryptoBuffer>& aAllowList,
+                   const WebAuthnExtensions& aExtensions);
+
+  nsresult
+  RelaxSameOrigin(const nsAString& aInputRpId, nsACString& aRelaxedRpId);
+
+  nsCOMPtr<nsPIDOMWindowInner> mParent;
+  nsString mOrigin;
+  Sequence<Authenticator> mAuthenticators;
+  bool mInitialized;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebAuthentication_h
rename from dom/u2f/WebAuthnAssertion.cpp
rename to dom/webauthn/WebAuthnAssertion.cpp
rename from dom/u2f/WebAuthnAssertion.h
rename to dom/webauthn/WebAuthnAssertion.h
rename from dom/u2f/WebAuthnAttestation.cpp
rename to dom/webauthn/WebAuthnAttestation.cpp
rename from dom/u2f/WebAuthnAttestation.h
rename to dom/webauthn/WebAuthnAttestation.h
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/WebAuthnRequest.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_dom_WebAuthnAsync_h
+#define mozilla_dom_WebAuthnAsync_h
+
+#include "mozilla/MozPromise.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+namespace dom {
+
+//extern mozilla::LazyLogModule gWebauthLog; // defined in U2F.cpp
+
+// WebAuthnRequest tracks the completion of a single WebAuthn request that
+// may run on multiple kinds of authenticators, and be subject to a deadline.
+template<class Success>
+class WebAuthnRequest {
+public:
+  WebAuthnRequest()
+    : mCancelled(false)
+    , mSuccess(false)
+    , mCountTokens(0)
+    , mTokensFailed(0)
+    , mReentrantMonitor("WebAuthnRequest")
+  {}
+
+  void AddActiveToken(const char* aCallSite)
+  {
+    // MOZ_LOG(gWebauthLog, LogLevel::Debug,
+    //        ("WebAuthnRequest is tracking a new token, called from [%s]",
+    //         aCallSite));
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    MOZ_ASSERT(!IsComplete());
+    mCountTokens += 1;
+  }
+
+  bool IsComplete()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    return mCancelled || mSuccess ||
+      (mCountTokens > 0 && mTokensFailed == mCountTokens);
+  }
+
+  void CancelNow()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    // It's possible for a race to cause CancelNow to get called after
+    // a success or a cancel. We only complete once.
+    if (IsComplete()) {
+      return;
+    }
+
+    mCancelled = true;
+    mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
+  }
+
+  void SetFailure(nsresult aError)
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    // It's possible for a race to cause SetFailure to get called after
+    // a success or a cancel. We only complete once.
+    if (IsComplete()) {
+      return;
+    }
+
+    mTokensFailed += 1;
+    MOZ_ASSERT(mTokensFailed <= mCountTokens);
+
+    if (mTokensFailed == mCountTokens) {
+      // Provide the final error as being indicitive of the whole set.
+      mPromise.Reject(aError, __func__);
+    }
+  }
+
+  void SetSuccess(const Success& aResult)
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    // It's possible for a race to cause multiple calls to SetSuccess
+    // in succession. We will only select the earliest.
+    if (IsComplete()) {
+      return;
+    }
+
+    mSuccess = true;
+    mPromise.Resolve(aResult, __func__);
+  }
+
+  void SetDeadline(TimeDuration aDeadline)
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    MOZ_ASSERT(!IsComplete());
+    // TODO: Monitor the deadline and stop with a timeout error if it expires.
+  }
+
+  RefPtr<MozPromise<Success, nsresult, false>> Ensure()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    MOZ_ASSERT(!IsComplete());
+    return mPromise.Ensure(__func__);
+  }
+
+  void CompleteTask()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+    if (mCountTokens == 0) {
+      // Special case for there being no tasks to complete
+      mPromise.Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
+    }
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebAuthnRequest)
+
+private:
+  ~WebAuthnRequest() {};
+
+  bool mCancelled;
+  bool mSuccess;
+  int mCountTokens;
+  int mTokensFailed;
+  ReentrantMonitor mReentrantMonitor;
+  MozPromiseHolder<MozPromise<Success, nsresult, false>> mPromise;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebAuthnAsync_h
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/moz.build
@@ -0,0 +1,38 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom += [
+    'NSSU2FTokenRemote.h',
+    'ScopedCredential.h',
+    'ScopedCredentialInfo.h',
+    'WebAuthentication.h',
+    'WebAuthnAssertion.h',
+    'WebAuthnAttestation.h',
+    'WebAuthnRequest.h',
+]
+
+UNIFIED_SOURCES += [
+    'NSSU2FTokenRemote.cpp',
+    'ScopedCredential.cpp',
+    'ScopedCredentialInfo.cpp',
+    'WebAuthentication.cpp',
+    'WebAuthnAssertion.cpp',
+    'WebAuthnAttestation.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+    '/dom/base',
+    '/dom/crypto',
+    '/security/manager/ssl',
+    '/security/pkix/include',
+    '/security/pkix/lib',
+]
+
+MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/tests/mochitest.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+support-files =
+  pkijs/asn1.js
+  pkijs/common.js
+  pkijs/x509_schema.js
+  pkijs/x509_simpl.js
+  u2futil.js
+
+[test_webauthn_loopback.html]
+skip-if = !e10s
+scheme = https
+[test_webauthn_no_token.html]
+skip-if = !e10s
+scheme = https
+[test_webauthn_make_credential.html]
+skip-if = !e10s
+scheme = https
+[test_webauthn_get_assertion.html]
+skip-if = !e10s
+scheme = https
+[test_webauthn_sameorigin.html]
+skip-if = !e10s
+scheme = https
\ No newline at end of file
new file mode 100644
--- /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.
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/tests/pkijs/README
@@ -0,0 +1,1 @@
+PKIjs and ASN1js are from https://pkijs.org/ and https://asn1js.org/.
\ No newline at end of file
new file mode 100644
--- /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_dat