Bug 1226928 - signature verification for content-signing, r=keeler,mayhemer
authorFranziskus Kiefer <franziskuskiefer@gmail.com>
Mon, 14 Mar 2016 11:56:35 +0100
changeset 288554 62415ab54662ea7151e6bcae16f0175ac9fa915e
parent 288553 6dff3e56ee1b746ecfd25c977f9c878ee10faed5
child 288555 99d90f0790854bf63853c34fed3141c5cf5cffae
push id30084
push userkwierso@gmail.com
push dateTue, 15 Mar 2016 00:39:07 +0000
treeherdermozilla-central@422077f61bcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler, mayhemer
bugs1226928
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1226928 - signature verification for content-signing, r=keeler,mayhemer
dom/security/ContentVerifier.cpp
dom/security/ContentVerifier.h
dom/security/moz.build
security/manager/ssl/ScopedNSSTypes.h
security/manager/ssl/moz.build
xpcom/base/ErrorList.h
new file mode 100644
--- /dev/null
+++ b/dom/security/ContentVerifier.cpp
@@ -0,0 +1,375 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "ContentVerifier.h"
+
+#include "mozilla/fallible.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsIInputStream.h"
+#include "nsIRequest.h"
+#include "nssb64.h"
+#include "nsSecurityHeaderParser.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStringStream.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+static LazyLogModule gContentVerifierPRLog("ContentVerifier");
+#define CSV_LOG(args) MOZ_LOG(gContentVerifierPRLog, LogLevel::Debug, args)
+
+// Content-Signature prefix
+const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00");
+
+NS_IMPL_ISUPPORTS(ContentVerifier, nsIStreamListener, nsISupports);
+
+nsresult
+ContentVerifier::Init(const nsAString& aContentSignatureHeader)
+{
+  mVks = Preferences::GetString("browser.newtabpage.remote.keys");
+
+  if (aContentSignatureHeader.IsEmpty() || mVks.IsEmpty()) {
+    CSV_LOG(
+      ("Content-Signature header and verification keys must not be empty!\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  nsresult rv = ParseContentSignatureHeader(aContentSignatureHeader);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
+  return CreateContext();
+}
+
+/**
+ * Implement nsIStreamListener
+ * We buffer the entire content here and kick off verification
+ */
+NS_METHOD
+AppendNextSegment(nsIInputStream* aInputStream, void* aClosure,
+                  const char* aRawSegment, uint32_t aToOffset, uint32_t aCount,
+                  uint32_t* outWrittenCount)
+{
+  FallibleTArray<nsCString>* decodedData =
+    static_cast<FallibleTArray<nsCString>*>(aClosure);
+  nsAutoCString segment(aRawSegment, aCount);
+  if (!decodedData->AppendElement(segment, fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  *outWrittenCount = aCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentVerifier::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentVerifier::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
+                               nsresult aStatus)
+{
+  // Verify the content:
+  // If this fails, we return an invalid signature error to load a fallback page.
+  // If everthing is good, we return a new stream to the next listener and kick
+  // that one of.
+  CSV_LOG(("VerifySignedContent, b64signature: %s\n", mSignature.get()));
+  CSV_LOG(("VerifySignedContent, key: \n[\n%s\n]\n", mKey.get()));
+  bool verified = false;
+  nsresult rv = End(&verified);
+  if (NS_FAILED(rv) || !verified || NS_FAILED(aStatus)) {
+    // cancel the request and return error
+    if (NS_FAILED(aStatus)) {
+      rv = aStatus;
+    } else {
+      rv = NS_ERROR_INVALID_SIGNATURE;
+    }
+    CSV_LOG(("failed to verify content\n"));
+    mNextListener->OnStartRequest(aRequest, aContext);
+    mNextListener->OnStopRequest(aRequest, aContext, rv);
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  CSV_LOG(("Successfully verified content signature.\n"));
+
+  // start the next listener
+  rv = mNextListener->OnStartRequest(aRequest, aContext);
+  if (NS_SUCCEEDED(rv)) {
+    // We emptied aInStr so we have to create a new one from buf to hand it
+    // to the consuming listener.
+    for (uint32_t i = 0; i < mContent.Length(); ++i) {
+      nsCOMPtr<nsIInputStream> oInStr;
+      rv = NS_NewCStringInputStream(getter_AddRefs(oInStr), mContent[i]);
+      if (NS_FAILED(rv)) {
+        break;
+      }
+      // let the next listener know that there is data in oInStr
+      rv = mNextListener->OnDataAvailable(aRequest, aContext, oInStr, 0,
+                                          mContent[i].Length());
+      if (NS_FAILED(rv)) {
+        break;
+      }
+    }
+  }
+
+  // propagate OnStopRequest and return
+  return mNextListener->OnStopRequest(aRequest, aContext, rv);
+}
+
+NS_IMETHODIMP
+ContentVerifier::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
+                                 nsIInputStream* aInputStream, uint64_t aOffset,
+                                 uint32_t aCount)
+{
+  // buffer the entire stream
+  uint32_t read;
+  nsresult rv = aInputStream->ReadSegments(AppendNextSegment, &mContent, aCount,
+                                           &read);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // update the signature verifier
+  return Update(mContent[mContent.Length()-1]);
+}
+
+/**
+ * ContentVerifier logic and utils
+ */
+
+nsresult
+ContentVerifier::GetVerificationKey(const nsAString& aKeyId)
+{
+  // get verification keys from the pref and see if we have |aKeyId|
+  nsCharSeparatedTokenizer tokenizerVK(mVks, ';');
+  while (tokenizerVK.hasMoreTokens()) {
+    nsDependentSubstring token = tokenizerVK.nextToken();
+    nsCharSeparatedTokenizer tokenizerKey(token, '=');
+    nsString prefKeyId;
+    if (tokenizerKey.hasMoreTokens()) {
+      prefKeyId = tokenizerKey.nextToken();
+    }
+    nsString key;
+    if (tokenizerKey.hasMoreTokens()) {
+      key = tokenizerKey.nextToken();
+    }
+    if (prefKeyId.Equals(aKeyId)) {
+      mKey.Assign(NS_ConvertUTF16toUTF8(key));
+      return NS_OK;
+    }
+  }
+
+  // we didn't find the appropriate key
+  return NS_ERROR_INVALID_SIGNATURE;
+}
+
+nsresult
+ContentVerifier::ParseContentSignatureHeader(
+  const nsAString& aContentSignatureHeader)
+{
+  // We only support p384 ecdsa according to spec
+  NS_NAMED_LITERAL_CSTRING(keyid_var, "keyid");
+  NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
+
+  nsAutoString contentSignature;
+  nsAutoString keyId;
+  nsAutoCString header = NS_ConvertUTF16toUTF8(aContentSignatureHeader);
+  nsSecurityHeaderParser parser(header.get());
+  nsresult rv = parser.Parse();
+  if (NS_FAILED(rv)) {
+    CSV_LOG(("ContentVerifier: could not parse ContentSignature header\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
+
+  for (nsSecurityHeaderDirective* directive = directives->getFirst();
+       directive != nullptr; directive = directive->getNext()) {
+    CSV_LOG(("ContentVerifier: found directive %s\n", directive->mName.get()));
+    if (directive->mName.Length() == keyid_var.Length() &&
+        directive->mName.EqualsIgnoreCase(keyid_var.get(),
+                                          keyid_var.Length())) {
+      if (!keyId.IsEmpty()) {
+        CSV_LOG(("ContentVerifier: found two keyIds\n"));
+        return NS_ERROR_INVALID_SIGNATURE;
+      }
+
+      CSV_LOG(("ContentVerifier: found a keyid directive\n"));
+      keyId = NS_ConvertUTF8toUTF16(directive->mValue);
+      rv = GetVerificationKey(keyId);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
+    }
+    if (directive->mName.Length() == signature_var.Length() &&
+        directive->mName.EqualsIgnoreCase(signature_var.get(),
+                                          signature_var.Length())) {
+      if (!contentSignature.IsEmpty()) {
+        CSV_LOG(("ContentVerifier: found two ContentSignatures\n"));
+        return NS_ERROR_INVALID_SIGNATURE;
+      }
+
+      CSV_LOG(("ContentVerifier: found a ContentSignature directive\n"));
+      contentSignature = NS_ConvertUTF8toUTF16(directive->mValue);
+      mSignature = directive->mValue;
+    }
+  }
+
+  // we have to ensure that we found a key and a signature at this point
+  if (mKey.IsEmpty()) {
+    CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find "
+             "an appropriate key.\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  if (mSignature.IsEmpty()) {
+    CSV_LOG(("ContentVerifier: got a Content-Signature header but didn't find "
+             "a signature.\n"));
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  return NS_OK;
+}
+
+/**
+ * Parse signature, public key, and algorithm data for input to verification
+ * functions in VerifyData and CreateContext.
+ *
+ * https://datatracker.ietf.org/doc/draft-thomson-http-content-signature/
+ * If aSignature is a content signature, the function returns
+ * NS_ERROR_INVALID_SIGNATURE if anything goes wrong. Only p384 with sha384
+ * is supported and aSignature is a raw signature (r||s).
+ */
+nsresult
+ContentVerifier::ParseInput(ScopedSECKEYPublicKey& aPublicKeyOut,
+                            ScopedSECItem& aSignatureItemOut,
+                            SECOidTag& aOidOut,
+                            const nsNSSShutDownPreventionLock&)
+{
+  // Base 64 decode the key
+  ScopedSECItem keyItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  if (!keyItem ||
+      !NSSBase64_DecodeBuffer(nullptr, keyItem,
+                              mKey.get(),
+                              mKey.Length())) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // Extract the public key from the keyItem
+  ScopedCERTSubjectPublicKeyInfo pki(
+    SECKEY_DecodeDERSubjectPublicKeyInfo(keyItem));
+  if (!pki) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  aPublicKeyOut = SECKEY_ExtractPublicKey(pki.get());
+
+  // in case we were not able to extract a key
+  if (!aPublicKeyOut) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // Base 64 decode the signature
+  ScopedSECItem rawSignatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  if (!rawSignatureItem ||
+      !NSSBase64_DecodeBuffer(nullptr, rawSignatureItem,
+                              mSignature.get(),
+                              mSignature.Length())) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // get signature object and oid
+  if (!aSignatureItemOut) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  // We have a raw ecdsa signature r||s so we have to DER-encode it first
+  // Note that we have to check rawSignatureItem->len % 2 here as
+  // DSAU_EncodeDerSigWithLen asserts this
+  if (rawSignatureItem->len == 0 || rawSignatureItem->len % 2 != 0) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  if (DSAU_EncodeDerSigWithLen(aSignatureItemOut, rawSignatureItem,
+                               rawSignatureItem->len) != SECSuccess) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+  aOidOut = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE;
+
+  return NS_OK;
+}
+
+/**
+ * Create a context for a signature verification.
+ * It sets signature, public key, and algorithms that should be used to verify
+ * the data. It also updates the verification buffer with the content-signature
+ * prefix.
+ */
+nsresult
+ContentVerifier::CreateContext()
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // Bug 769521: We have to change b64 url to regular encoding as long as we
+  // don't have a b64 url decoder. This should change soon, but in the meantime
+  // we have to live with this.
+  mSignature.ReplaceChar('-', '+');
+  mSignature.ReplaceChar('_', '/');
+
+  ScopedSECKEYPublicKey publicKey;
+  ScopedSECItem signatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
+  SECOidTag oid;
+  nsresult rv = ParseInput(publicKey, signatureItem, oid, locker);
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  mCx = UniqueVFYContext(VFY_CreateContext(publicKey, signatureItem, oid, NULL));
+  if (!mCx) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  if (VFY_Begin(mCx.get()) != SECSuccess) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  // add the prefix to the verification buffer
+  return Update(kPREFIX);
+}
+
+/**
+ * Add data to the context that should be verified.
+ */
+nsresult
+ContentVerifier::Update(const nsACString& aData)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  if (!aData.IsEmpty()) {
+    if (VFY_Update(mCx.get(),
+                   (const unsigned char*)nsPromiseFlatCString(aData).get(),
+                   aData.Length()) != SECSuccess) {
+      return NS_ERROR_INVALID_SIGNATURE;
+    }
+  }
+
+  return NS_OK;
+}
+
+/**
+ * Finish signature verification and return the result in _retval.
+ */
+nsresult
+ContentVerifier::End(bool* _retval)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_INVALID_SIGNATURE;
+  }
+
+  *_retval = (VFY_End(mCx.get()) == SECSuccess);
+
+  return NS_OK;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/security/ContentVerifier.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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_ContentVerifier_h
+#define mozilla_dom_ContentVerifier_h
+
+#include "nsCOMPtr.h"
+#include "nsIObserver.h"
+#include "nsIStreamListener.h"
+#include "nsNSSShutDown.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "ScopedNSSTypes.h"
+
+/**
+ * Mediator intercepting OnStartRequest in nsHttpChannel, blocks until all
+ * data is read from the input stream, verifies the content signature and
+ * releases the request to the next listener if the verification is successful.
+ * If the verification fails or anything else goes wrong, a
+ * NS_ERROR_INVALID_SIGNATURE is thrown.
+ */
+class ContentVerifier : public nsIStreamListener
+                      , public nsNSSShutDownObject
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIREQUESTOBSERVER
+
+  explicit ContentVerifier(nsIStreamListener* aMediatedListener,
+                           nsISupports* aMediatedContext)
+    : mNextListener(aMediatedListener)
+    , mContext(aMediatedContext)
+    , mCx(nullptr) {}
+
+  nsresult Init(const nsAString& aContentSignatureHeader);
+
+  // nsNSSShutDownObject
+  virtual void virtualDestroyNSSReference() override
+  {
+    destructorSafeDestroyNSSReference();
+  }
+
+protected:
+  virtual ~ContentVerifier()
+  {
+    nsNSSShutDownPreventionLock locker;
+    if (isAlreadyShutDown()) {
+      return;
+    }
+    destructorSafeDestroyNSSReference();
+    shutdown(calledFromObject);
+  }
+
+  void destructorSafeDestroyNSSReference()
+  {
+    mCx = nullptr;
+  }
+
+private:
+  nsresult ParseContentSignatureHeader(const nsAString& aContentSignatureHeader);
+  nsresult GetVerificationKey(const nsAString& aKeyId);
+
+  // utility function to parse input before put into verification functions
+  nsresult ParseInput(mozilla::ScopedSECKEYPublicKey& aPublicKeyOut,
+                      mozilla::ScopedSECItem& aSignatureItemOut,
+                      SECOidTag& aOidOut,
+                      const nsNSSShutDownPreventionLock&);
+
+  // create a verifier context and store it in mCx
+  nsresult CreateContext();
+
+  // Adds data to the context that was used to generate the signature.
+  nsresult Update(const nsACString& aData);
+
+  // Finalises the signature and returns the result of the signature
+  // verification.
+  nsresult End(bool* _retval);
+
+  // content and next listener for nsIStreamListener
+  nsCOMPtr<nsIStreamListener> mNextListener;
+  nsCOMPtr<nsISupports> mContext;
+
+  // verifier context for incrementel verifications
+  mozilla::UniqueVFYContext mCx;
+  // buffered content to verify
+  FallibleTArray<nsCString> mContent;
+  // signature to verify
+  nsCString mSignature;
+  // verification key
+  nsCString mKey;
+  // verification key preference
+  nsString mVks;
+};
+
+#endif /* mozilla_dom_ContentVerifier_h */
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -2,30 +2,32 @@
 # 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/.
 
 TEST_DIRS += ['test']
 
 EXPORTS.mozilla.dom += [
+    'ContentVerifier.h',
     'nsContentSecurityManager.h',
     'nsCSPContext.h',
     'nsCSPService.h',
     'nsCSPUtils.h',
     'nsMixedContentBlocker.h',
     'SRICheck.h',
     'SRIMetadata.h',
 ]
 
 EXPORTS += [
     'nsContentSecurityManager.h',
 ]
 
 UNIFIED_SOURCES += [
+    'ContentVerifier.cpp',
     'nsContentSecurityManager.cpp',
     'nsCSPContext.cpp',
     'nsCSPParser.cpp',
     'nsCSPService.cpp',
     'nsCSPUtils.cpp',
     'nsMixedContentBlocker.cpp',
     'SRICheck.cpp',
     'SRIMetadata.cpp',
--- a/security/manager/ssl/ScopedNSSTypes.h
+++ b/security/manager/ssl/ScopedNSSTypes.h
@@ -289,16 +289,21 @@ inline void SECOID_DestroyAlgorithmID_tr
   return SECOID_DestroyAlgorithmID(a, true);
 }
 
 inline void SECKEYEncryptedPrivateKeyInfo_true(SECKEYEncryptedPrivateKeyInfo * epki)
 {
   return SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
 }
 
+inline void VFY_DestroyContext_true(VFYContext * ctx)
+{
+  VFY_DestroyContext(ctx, true);
+}
+
 } // namespace internal
 
 // Deprecated: use the equivalent UniquePtr templates instead.
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECItem,
                                           SECItem,
                                           internal::SECITEM_FreeItem_true)
 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSECKEYPrivateKey,
                                           SECKEYPrivateKey,
@@ -356,11 +361,14 @@ MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(Un
                                       SECItem,
                                       internal::SECITEM_FreeItem_true)
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey,
                                       SECKEYPublicKey,
                                       SECKEY_DestroyPublicKey)
 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECMODModule,
                                       SECMODModule,
                                       SECMOD_DestroyModule)
+MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueVFYContext,
+                                      VFYContext,
+                                      internal::VFY_DestroyContext_true)
 } // namespace mozilla
 
 #endif // mozilla_ScopedNSSTypes_h
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -56,16 +56,17 @@ EXPORTS += [
     'nsClientAuthRemember.h',
     'nsCrypto.h',
     'nsNSSCallbacks.h',
     'nsNSSCertificate.h',
     'nsNSSComponent.h',
     'nsNSSHelper.h',
     'nsNSSShutDown.h',
     'nsRandomGenerator.h',
+    'nsSecurityHeaderParser.h',
     'NSSErrorsService.h',
     'ScopedNSSTypes.h',
     'SharedCertVerifier.h',
 ]
 
 EXPORTS.mozilla += [
     'DataStorage.h',
     'PublicSSL.h',
--- a/xpcom/base/ErrorList.h
+++ b/xpcom/base/ErrorList.h
@@ -174,16 +174,20 @@
   ERROR(NS_ERROR_UNKNOWN_PROTOCOL,                    FAILURE(18)),
   /* The content encoding of the source document was incorrect, for example
    * returning a plain HTML document advertised as Content-Encoding: gzip */
   ERROR(NS_ERROR_INVALID_CONTENT_ENCODING,            FAILURE(27)),
   /* A transport level corruption was found in the source document. for example
    * a document with a calculated checksum that does not match the Content-MD5
    * http header. */
   ERROR(NS_ERROR_CORRUPTED_CONTENT,                   FAILURE(29)),
+  /* A content signature verification failed for some reason. This can be either
+   * an actual verification error, or any other error that led to the fact that
+   * a content signature that was expected couldn't be verified. */
+  ERROR(NS_ERROR_INVALID_SIGNATURE,                   FAILURE(58)),
   /* While parsing for the first component of a header field using syntax as in
    * Content-Disposition or Content-Type, the first component was found to be
    * empty, such as in: Content-Disposition: ; filename=foo */
   ERROR(NS_ERROR_FIRST_HEADER_FIELD_COMPONENT_EMPTY,  FAILURE(34)),
   /* Returned from nsIChannel::asyncOpen when trying to open the channel again
    * (reopening is not supported). */
   ERROR(NS_ERROR_ALREADY_OPENED,                      FAILURE(73)),