зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1226928 - signature verification for content-signing, r=keeler,mayhemer
This commit is contained in:
Родитель
de6c5e883a
Коммит
bd54ab19d3
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -7,6 +7,7 @@
|
|||
TEST_DIRS += ['test']
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'ContentVerifier.h',
|
||||
'nsContentSecurityManager.h',
|
||||
'nsCSPContext.h',
|
||||
'nsCSPService.h',
|
||||
|
@ -21,6 +22,7 @@ EXPORTS += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ContentVerifier.cpp',
|
||||
'nsContentSecurityManager.cpp',
|
||||
'nsCSPContext.cpp',
|
||||
'nsCSPParser.cpp',
|
||||
|
|
|
@ -294,6 +294,11 @@ inline void SECKEYEncryptedPrivateKeyInfo_true(SECKEYEncryptedPrivateKeyInfo * e
|
|||
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.
|
||||
|
@ -361,6 +366,9 @@ MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey,
|
|||
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
|
||||
|
|
|
@ -61,6 +61,7 @@ EXPORTS += [
|
|||
'nsNSSHelper.h',
|
||||
'nsNSSShutDown.h',
|
||||
'nsRandomGenerator.h',
|
||||
'nsSecurityHeaderParser.h',
|
||||
'NSSErrorsService.h',
|
||||
'ScopedNSSTypes.h',
|
||||
'SharedCertVerifier.h',
|
||||
|
|
|
@ -179,6 +179,10 @@
|
|||
* 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 */
|
||||
|
|
Загрузка…
Ссылка в новой задаче