Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 19 Jan 2017 22:04:52 -0800
changeset 375242 aa3e49299a3aa5cb0db570532e3df9e75d30c2d1
parent 375224 fe22af79bacf6db526737536eab551caf68ba440 (current diff)
parent 375241 e2860876fc43daab36c5dbc920dcf4d298923de2 (diff)
child 375243 e674ae0954df1bf18645ef7dba91365b8282f27e
child 375256 24e2f44013cc68b2d8cca78ce6655ae39b121362
child 375277 4ec5bc4fff15bf293a7118065df335847d0e2f96
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)
reviewersmerge
milestone53.0a1
first release with
nightly mac
aa3e49299a3a / 53.0a1 / 20170120030214 / files
nightly win32
aa3e49299a3a / 53.0a1 / 20170120030214 / files
nightly win64
aa3e49299a3a / 53.0a1 / 20170120030214 / files
nightly linux32
nightly linux64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
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
testing/mochitest/runtests.py
--- 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
+            };