Backed out changeset 21d8bb5af7b4 (bug 1263793) for leaks in various jobs CLOSED TREE

This commit is contained in:
Wes Kocher 2016-07-20 11:16:37 -07:00
Родитель 2f160622c6
Коммит e2d9911273
16 изменённых файлов: 633 добавлений и 806 удалений

Просмотреть файл

@ -7,47 +7,41 @@
#include "mozilla/fallible.h"
#include "mozilla/Logging.h"
#include "MainThreadUtils.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)
NS_IMPL_ISUPPORTS(ContentVerifier,
nsIContentSignatureReceiverCallback,
nsIStreamListener);
// Content-Signature prefix
const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00");
NS_IMPL_ISUPPORTS(ContentVerifier, nsIStreamListener, nsISupports);
nsresult
ContentVerifier::Init(const nsACString& aContentSignatureHeader,
nsIRequest* aRequest, nsISupports* aContext)
ContentVerifier::Init(const nsAString& aContentSignatureHeader)
{
MOZ_ASSERT(NS_IsMainThread());
if (aContentSignatureHeader.IsEmpty()) {
CSV_LOG(("Content-Signature header must not be empty!\n"));
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;
}
// initialise the content signature "service"
nsresult rv;
mVerifier =
do_CreateInstance("@mozilla.org/security/contentsignatureverifier;1", &rv);
if (NS_FAILED(rv) || !mVerifier) {
return NS_ERROR_INVALID_SIGNATURE;
}
// Keep references to the request and context. We need them in FinishSignature
// and the ContextCreated callback.
mContentRequest = aRequest;
mContentContext = aContext;
return mVerifier->CreateContextWithoutCertChain(
this, aContentSignatureHeader,
NS_LITERAL_CSTRING("remote-newtab-signer.mozilla.org"));
nsresult rv = ParseContentSignatureHeader(aContentSignatureHeader);
NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
return CreateContext();
}
/**
@ -61,7 +55,7 @@ AppendNextSegment(nsIInputStream* aInputStream, void* aClosure,
{
FallibleTArray<nsCString>* decodedData =
static_cast<FallibleTArray<nsCString>*>(aClosure);
nsDependentCSubstring segment(aRawSegment, aCount);
nsAutoCString segment(aRawSegment, aCount);
if (!decodedData->AppendElement(segment, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -69,57 +63,9 @@ AppendNextSegment(nsIInputStream* aInputStream, void* aClosure,
return NS_OK;
}
void
ContentVerifier::FinishSignature()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIStreamListener> nextListener;
nextListener.swap(mNextListener);
// 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 off.
bool verified = false;
nsresult rv = NS_OK;
// If the content signature check fails, stop the load
// and return a signature error. NSS resources are freed by the
// ContentSignatureVerifier on destruction.
if (NS_FAILED(mVerifier->End(&verified)) || !verified) {
CSV_LOG(("failed to verify content\n"));
(void)nextListener->OnStopRequest(mContentRequest, mContentContext,
NS_ERROR_INVALID_SIGNATURE);
return;
}
CSV_LOG(("Successfully verified content signature.\n"));
// We emptied the input stream so we have to create a new one from mContent
// to hand it to the consuming listener.
uint64_t offset = 0;
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 = nextListener->OnDataAvailable(mContentRequest, mContentContext, oInStr,
offset, mContent[i].Length());
offset += mContent[i].Length();
if (NS_FAILED(rv)) {
break;
}
}
// propagate OnStopRequest and return
nextListener->OnStopRequest(mContentRequest, mContentContext, rv);
}
NS_IMETHODIMP
ContentVerifier::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
MOZ_CRASH("This OnStartRequest should've never been called!");
return NS_OK;
}
@ -127,28 +73,50 @@ NS_IMETHODIMP
ContentVerifier::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
nsresult aStatus)
{
// If we don't have a next listener, we handed off this request already.
// Return, there's nothing to do here.
if (!mNextListener) {
return NS_OK;
// 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;
}
}
}
if (NS_FAILED(aStatus)) {
CSV_LOG(("Stream failed\n"));
nsCOMPtr<nsIStreamListener> nextListener;
nextListener.swap(mNextListener);
return nextListener->OnStopRequest(aRequest, aContext, aStatus);
}
mContentRead = true;
// If the ContentSignatureVerifier is initialised, finish the verification.
if (mContextCreated) {
FinishSignature();
return aStatus;
}
return NS_OK;
// propagate OnStopRequest and return
return mNextListener->OnStopRequest(aRequest, aContext, rv);
}
NS_IMETHODIMP
@ -164,63 +132,244 @@ ContentVerifier::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
return rv;
}
// Update the signature verifier if the context has been created.
if (mContextCreated) {
return mVerifier->Update(mContent.LastElement());
}
return NS_OK;
// update the signature verifier
return Update(mContent[mContent.Length()-1]);
}
NS_IMETHODIMP
ContentVerifier::ContextCreated(bool successful)
/**
* ContentVerifier logic and utils
*/
nsresult
ContentVerifier::GetVerificationKey(const nsAString& aKeyId)
{
MOZ_ASSERT(NS_IsMainThread());
if (!successful) {
// If we don't have a next listener, the request has been handed off already.
if (!mNextListener) {
// 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;
}
// Get local reference to mNextListener and null it to ensure that we don't
// call it twice.
nsCOMPtr<nsIStreamListener> nextListener;
nextListener.swap(mNextListener);
// Make sure that OnStartRequest was called and we have a request.
MOZ_ASSERT(mContentRequest);
// In this case something went wrong with the cert. Let's stop this load.
CSV_LOG(("failed to get a valid cert chain\n"));
if (mContentRequest && nextListener) {
mContentRequest->Cancel(NS_ERROR_INVALID_SIGNATURE);
nsresult rv = nextListener->OnStopRequest(mContentRequest, mContentContext,
NS_ERROR_INVALID_SIGNATURE);
mContentRequest = nullptr;
mContentContext = nullptr;
return rv;
}
// We should never get here!
MOZ_ASSERT_UNREACHABLE(
"ContentVerifier was used without getting OnStartRequest!");
return NS_OK;
}
// In this case the content verifier is initialised and we have to feed it
// the buffered content.
mContextCreated = true;
for (size_t i = 0; i < mContent.Length(); ++i) {
if (NS_FAILED(mVerifier->Update(mContent[i]))) {
// Bail out if this fails. We can't return an error here, but if this
// failed, NS_ERROR_INVALID_SIGNATURE is returned in FinishSignature.
break;
// 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 read all content, let's verify the signature.
if (mContentRead) {
FinishSignature();
// 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;
}

Просмотреть файл

@ -7,11 +7,12 @@
#define mozilla_dom_ContentVerifier_h
#include "nsCOMPtr.h"
#include "nsIContentSignatureVerifier.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
@ -21,44 +22,77 @@
* NS_ERROR_INVALID_SIGNATURE is thrown.
*/
class ContentVerifier : public nsIStreamListener
, public nsIContentSignatureReceiverCallback
, public nsNSSShutDownObject
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSICONTENTSIGNATURERECEIVERCALLBACK
explicit ContentVerifier(nsIStreamListener* aMediatedListener,
nsISupports* aMediatedContext)
: mNextListener(aMediatedListener)
, mContextCreated(false)
, mContentRead(false) {}
, mContext(aMediatedContext)
, mCx(nullptr) {}
nsresult Init(const nsACString& aContentSignatureHeader, nsIRequest* aRequest,
nsISupports* aContext);
nsresult Init(const nsAString& aContentSignatureHeader);
// nsNSSShutDownObject
virtual void virtualDestroyNSSReference() override
{
destructorSafeDestroyNSSReference();
}
protected:
virtual ~ContentVerifier() {}
virtual ~ContentVerifier()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
destructorSafeDestroyNSSReference();
shutdown(calledFromObject);
}
void destructorSafeDestroyNSSReference()
{
mCx = nullptr;
}
private:
void FinishSignature();
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);
// buffered content to verify
FallibleTArray<nsCString> mContent;
// content and next listener for nsIStreamListener
nsCOMPtr<nsIStreamListener> mNextListener;
// the verifier
nsCOMPtr<nsIContentSignatureVerifier> mVerifier;
// holding a pointer to the content request and context to resume/cancel it
nsCOMPtr<nsIRequest> mContentRequest;
nsCOMPtr<nsISupports> mContentContext;
// Semaphors to indicate that the verifying context was created, the entire
// content was read resp. The context gets created by ContentSignatureVerifier
// and mContextCreated is set in the ContextCreated callback. The content is
// read, i.e. mContentRead is set, when the content OnStopRequest is called.
bool mContextCreated;
bool mContentRead;
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 */

Просмотреть файл

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsAttrValue.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsCSPUtils.h"
#include "nsDebug.h"

Просмотреть файл

@ -10,10 +10,7 @@ support-files =
file_about_newtab_broken_signature
file_about_newtab_sri.html
file_about_newtab_sri_signature
goodChain.pem
head.js
script.js
style.css
[browser_verify_content_about_newtab.js]
[browser_verify_content_about_newtab2.js]

Просмотреть файл

@ -1,9 +1,78 @@
/*
* Test Content-Signature for remote about:newtab
* - Bug 1226928 - allow about:newtab to load remote content
*
* This tests content-signature verification on remote about:newtab in the
* following cases (see TESTS, all failed loads display about:blank fallback):
* - good case (signature should verify and correct page is displayed)
* - reload of newtab when the siganture was invalidated after the last correct
* load
* - malformed content-signature header
* - malformed keyid directive
* - malformed p384ecdsa directive
* - wrong signature (this is not a siganture for the delivered document)
* - invalid signature (this is not even a signature)
* - loading a file that doesn't fit the key or signature
* - cache poisoning (load a malicious remote page not in newtab, subsequent
* newtab load has to load the fallback)
*/
const ABOUT_NEWTAB_URI = "about:newtab";
const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
const URI_GOOD = BASE + "sig=good&key=good&file=good&header=good";
const INVALIDATE_FILE = BASE + "invalidateFile=yep";
const VALIDATE_FILE = BASE + "validateFile=yep";
const URI_HEADER_BASE = BASE + "sig=good&key=good&file=good&header=";
const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInKeyid";
const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";
const URI_BAD_SIG = BASE + "sig=bad&key=good&file=good&header=good";
const URI_BROKEN_SIG = BASE + "sig=broken&key=good&file=good&header=good";
const URI_BAD_KEY = BASE + "sig=good&key=bad&file=good&header=good";
const URI_BAD_FILE = BASE + "sig=good&key=good&file=bad&header=good";
const URI_BAD_ALL = BASE + "sig=bad&key=bad&file=bad&header=bad";
const URI_BAD_CSP = BASE + "sig=bad-csp&key=good&file=bad-csp&header=good";
const URI_BAD_FILE_CACHED = BASE + "sig=good&key=good&file=bad&header=good&cached=true";
const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
const ABOUT_BLANK = "<head></head><body></body>";
const URI_CLEANUP = BASE + "cleanup=true";
const CLEANUP_DONE = "Done";
const URI_SRI = BASE + "sig=sri&key=good&file=sri&header=good";
const STYLESHEET_WITHOUT_SRI_BLOCKED = "Stylesheet without SRI blocked";
const STYLESHEET_WITH_SRI_BLOCKED = "Stylesheet with SRI blocked";
const STYLESHEET_WITH_SRI_LOADED = "Stylesheet with SRI loaded";
const SCRIPT_WITHOUT_SRI_BLOCKED = "Script without SRI blocked";
const SCRIPT_WITH_SRI_BLOCKED = "Script with SRI blocked";
const SCRIPT_WITH_SRI_LOADED = "Script with SRI loaded";
const CSP_TEST_SUCCESS_STRING = "CSP violation test succeeded.";
// Needs to sync with pref "security.signed_content.CSP.default".
const SIGNED_CONTENT_CSP = `{"csp-policies":[{"report-only":false,"script-src":["https://example.com","'unsafe-inline'"],"style-src":["https://example.com"]}]}`;
const TESTS = [
// { newtab (aboutURI) or regular load (url) : url,
// testStrings : expected strings in the loaded page }
{ "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
{ "aboutURI" : URI_ERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_KEYERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_SIGERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_NO_HEADER, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BAD_SIG, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BROKEN_SIG, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BAD_KEY, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BAD_FILE, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BAD_ALL, "testStrings" : [ABOUT_BLANK] },
{ "url" : URI_BAD_FILE_CACHED, "testStrings" : [BAD_ABOUT_STRING] },
{ "aboutURI" : URI_BAD_FILE_CACHED, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
@ -17,4 +86,158 @@ const TESTS = [
{ "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] },
];
add_task(runTests);
var browser = null;
var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
.getService(Ci.nsIAboutNewTabService);
function pushPrefs(...aPrefs) {
return new Promise((resolve) => {
SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
});
}
/*
* run tests with input from TESTS
*/
function doTest(aExpectedStrings, reload, aUrl, aNewTabPref) {
// set about:newtab location for this test if it's a newtab test
if (aNewTabPref) {
aboutNewTabService.newTabURL = aNewTabPref;
}
// set prefs
yield pushPrefs(
["browser.newtabpage.remote.content-signing-test", true],
["browser.newtabpage.remote", true], [
"browser.newtabpage.remote.keys",
"RemoteNewTabNightlyv0=BO9QHuP6E2eLKybql8iuD4o4Np9YFDfW3D+k" +
"a70EcXXTqZcikc7Am1CwyP1xBDTpEoe6gb9SWzJmaDW3dNh1av2u90VkUM" +
"B7aHIrImjTjLNg/1oC8GRcTKM4+WzbKF00iA==;OtherKey=eKQJ2fNSId" +
"CFzL6N326EzZ/5LCeFU5eyq3enwZ5MLmvOw+3gycr4ZVRc36/EiSPsQYHE" +
"3JvJs1EKs0QCaguHFOZsHwqXMPicwp/gLdeYbuOmN2s1SEf/cxw8GtcxSA" +
"kG;RemoteNewTab=MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4k3FmG7dFo" +
"Ot3Tuzl76abTRtK8sb/r/ibCSeVKa96RbrOX2ciscz/TT8wfqBYS/8cN4z" +
"Me1+f7wRmkNrCUojZR1ZKmYM2BeiUOMlMoqk2O7+uwsn1DwNQSYP58TkvZt6"
]);
if (aNewTabPref === URI_BAD_CSP) {
// Use stricter CSP to test CSP violation.
yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
} else {
// Use weaker CSP to test normal content.
yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
}
// start the test
yield BrowserTestUtils.withNewTab({
gBrowser,
url: aUrl,
},
function * (browser) {
// check if everything's set correct for testing
ok(Services.prefs.getBoolPref(
"browser.newtabpage.remote.content-signing-test"),
"sanity check: remote newtab signing test should be used");
ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
"sanity check: remote newtab should be used");
// we only check this if we really do a newtab test
if (aNewTabPref) {
ok(aboutNewTabService.overridden,
"sanity check: default URL for about:newtab should be overriden");
is(aboutNewTabService.newTabURL, aNewTabPref,
"sanity check: default URL for about:newtab should return the new URL");
}
// Every valid remote newtab page must have built-in CSP.
let shouldHaveCSP = ((aUrl === ABOUT_NEWTAB_URI) &&
(aNewTabPref === URI_GOOD || aNewTabPref === URI_SRI));
if (shouldHaveCSP) {
is(browser.contentDocument.nodePrincipal.cspJSON, SIGNED_CONTENT_CSP,
"Valid remote newtab page must have built-in CSP.");
}
yield ContentTask.spawn(
browser, aExpectedStrings, function * (aExpectedStrings) {
for (let expectedString of aExpectedStrings) {
ok(content.document.documentElement.innerHTML.includes(expectedString),
"Expect the following value in the result\n" + expectedString +
"\nand got " + content.document.documentElement.innerHTML);
}
});
// for good test cases we check if a reload fails if the remote page
// changed from valid to invalid in the meantime
if (reload) {
yield BrowserTestUtils.withNewTab({
gBrowser,
url: INVALIDATE_FILE,
},
function * (browser2) {
yield ContentTask.spawn(browser2, null, function * () {
ok(content.document.documentElement.innerHTML.includes("Done"),
"Expect the following value in the result\n" + "Done" +
"\nand got " + content.document.documentElement.innerHTML);
});
}
);
browser.reload();
yield BrowserTestUtils.browserLoaded(browser);
let expectedStrings = [ABOUT_BLANK];
if (aNewTabPref == URI_SRI) {
expectedStrings = [
STYLESHEET_WITHOUT_SRI_BLOCKED,
STYLESHEET_WITH_SRI_BLOCKED,
SCRIPT_WITHOUT_SRI_BLOCKED,
SCRIPT_WITH_SRI_BLOCKED
];
}
yield ContentTask.spawn(browser, expectedStrings,
function * (expectedStrings) {
for (let expectedString of expectedStrings) {
ok(content.document.documentElement.innerHTML.includes(expectedString),
"Expect the following value in the result\n" + expectedString +
"\nand got " + content.document.documentElement.innerHTML);
}
}
);
yield BrowserTestUtils.withNewTab({
gBrowser,
url: VALIDATE_FILE,
},
function * (browser2) {
yield ContentTask.spawn(browser2, null, function * () {
ok(content.document.documentElement.innerHTML.includes("Done"),
"Expect the following value in the result\n" + "Done" +
"\nand got " + content.document.documentElement.innerHTML);
});
}
);
}
}
);
}
add_task(function * test() {
// run tests from TESTS
for (let i = 0; i < TESTS.length; i++) {
let testCase = TESTS[i];
let url = "", aNewTabPref = "";
let reload = false;
var aExpectedStrings = testCase.testStrings;
if (testCase.aboutURI) {
url = ABOUT_NEWTAB_URI;
aNewTabPref = testCase.aboutURI;
if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) {
reload = true;
}
} else {
url = testCase.url;
}
yield doTest(aExpectedStrings, reload, url, aNewTabPref);
}
});

Просмотреть файл

@ -1,19 +0,0 @@
const TESTS = [
// { newtab (aboutURI) or regular load (url) : url,
// testStrings : expected strings in the loaded page }
{ "aboutURI" : URI_GOOD, "testStrings" : [GOOD_ABOUT_STRING] },
{ "aboutURI" : URI_ERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_KEYERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_SIGERROR_HEADER, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_NO_HEADER, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BAD_SIG, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BROKEN_SIG, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BAD_X5U, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_HTTP_X5U, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BAD_FILE, "testStrings" : [ABOUT_BLANK] },
{ "aboutURI" : URI_BAD_ALL, "testStrings" : [ABOUT_BLANK] },
{ "url" : URI_CLEANUP, "testStrings" : [CLEANUP_DONE] },
];
add_task(runTests);

Просмотреть файл

@ -1 +1 @@
oiypz3lb-IyJsmKNsnlp2zDrqncste8yONn9WUE6ksgJWMhSEQ9lp8vRqN0W3JPwJb6uSk16RI-tDv7uy0jxon5jL1BZpqlqIpvimg7FCQEedMKoHZwtE9an-e95sOTd
8qXVAqzuF3TsF6C750u_v_JiRu90WJXf_0xT9x0S4Fgmvolgtfu-KSWq3lYpmk2dxO8u64zaHM3iguZdWAqcSL82RFtV7OPiprt16omCbHCKfVi-Bt_rXILRlexgmRl_

Просмотреть файл

@ -1 +1 @@
-mqpvTYdZX4HYQDW1nScojL7ICw5yj8UF2gzxyLbSCx9UIfHH-gWZ40F_PFtqjHxoC1J3dHDb3VedVhOYczdaLrNKbRvPrlnkdGx7Rl8qEBrtZpF1py1Z9uAGoCrgUHa
XBKzej3i6TAFZc3VZsuCekn-4dYWJBE4-b3OOtKrOV-JIzIvAnAhnOV1aj-kEm07kh-FciIxV-Xk2QUQlRQzHO7oW7E4mXkMKkbbAcvL0CFrItTObhfhKnBnpAE9ql1O

Просмотреть файл

@ -1 +1 @@
yoIyAYiiEzdP1zpkRy3KaqdsjUy62Notku89cytwVwcH0x6fKsMCdM-df1wbk9N28CSTaIOW5kcSenFy5K3nU-zPIoqZDjQo6aSjF8hF6lrw1a1xbhfl9K3g4YJsuWsO
i5jOnrZWwyNwrTcIjfJ6fUR-8MhhvhtMvQbdrUD7j8aHTybNolv25v9NwJAT6rVU6kgkxmD_st9Kla086CQmzYQdLhKfzgLbTDXz0-1j23fQnyjsP1_4MNIu2xTea11p

Просмотреть файл

@ -1,9 +1,4 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
// sjs for remote about:newtab (bug 1226928)
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/NetUtil.jsm");
@ -17,15 +12,14 @@ const goodFileBase = path + goodFileName;
const goodFile = FileUtils.getDir("TmpD", [], true);
goodFile.append(goodFileName);
const goodSignature = path + "file_about_newtab_good_signature";
const goodX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\"";
const goodKeyId = "RemoteNewTab";
const scriptFileName = "script.js";
const cssFileName = "style.css";
const badFile = path + "file_about_newtab_bad.html";
const brokenSignature = path + "file_about_newtab_broken_signature";
const badSignature = path + "file_about_newtab_bad_signature";
const badX5UString = "\"https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=bad\"";
const httpX5UString = "\"http://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?x5u=default\"";
const badKeyId = "OldRemoteNewTabKey";
const sriFile = path + "file_about_newtab_sri.html";
const sriSignature = path + "file_about_newtab_sri_signature";
@ -33,14 +27,6 @@ const sriSignature = path + "file_about_newtab_sri_signature";
const badCspFile = path + "file_about_newtab_bad_csp.html";
const badCspSignature = path + "file_about_newtab_bad_csp_signature";
// This cert chain is copied from
// security/manager/ssl/tests/unit/test_content_signing/
// using the certificates
// * content_signing_remote_newtab_ee.pem
// * content_signing_int.pem
// * content_signing_root.pem
const goodCertChainPath = path + "goodChain.pem";
const tempFileNames = [goodFileName, scriptFileName, cssFileName];
// we copy the file to serve as newtab to a temp directory because
@ -129,7 +115,7 @@ function cleanupTestFiles() {
*/
function handleRequest(request, response) {
let params = new URLSearchParams(request.queryString);
let x5uType = params.get("x5u");
let keyType = params.get("key");
let signatureType = params.get("sig");
let fileType = params.get("file");
let headerType = params.get("header");
@ -137,7 +123,6 @@ function handleRequest(request, response) {
let invalidateFile = params.get("invalidateFile");
let validateFile = params.get("validateFile");
let resource = params.get("resource");
let x5uParam = params.get("x5u");
if (params.get("cleanup")) {
cleanupTestFiles();
@ -186,14 +171,6 @@ function handleRequest(request, response) {
return;
}
// we have to return the certificate chain on request for the x5u parameter
if (x5uParam && x5uParam == "default") {
response.setHeader("Cache-Control", "max-age=216000", false);
response.setHeader("Content-Type", "text/plain", false);
response.write(loadFile(getFileName(goodCertChainPath, "CurWorkD")));
return;
}
// avoid confusing cache behaviours
if (!cached) {
response.setHeader("Cache-Control", "no-cache", false);
@ -209,13 +186,11 @@ function handleRequest(request, response) {
* value has to be indicated in the url.
*/
let csHeader = "";
let x5uString = goodX5UString;
let keyId = goodKeyId;
let signature = goodSignature;
let file = goodFile;
if (x5uType == "bad") {
x5uString = badX5UString;
} else if (x5uType == "http") {
x5uString = httpX5UString;
if (keyType == "bad") {
keyId = badKeyId;
}
if (signatureType == "bad") {
signature = badSignature;
@ -236,19 +211,19 @@ function handleRequest(request, response) {
if (headerType == "good") {
// a valid content-signature header
csHeader = "x5u=" + x5uString + ";p384ecdsa=" +
csHeader = "keyid=" + keyId + ";p384ecdsa=" +
loadFile(getFileName(signature, "CurWorkD"));
} else if (headerType == "error") {
// this content-signature header is missing ; before p384ecdsa
csHeader = "x5u=" + x5uString + "p384ecdsa=" +
csHeader = "keyid=" + keyId + "p384ecdsa=" +
loadFile(getFileName(signature, "CurWorkD"));
} else if (headerType == "errorInX5U") {
} else if (headerType == "errorInKeyid") {
// this content-signature header is missing the keyid directive
csHeader = "x6u=" + x5uString + ";p384ecdsa=" +
csHeader = "keid=" + keyId + ";p384ecdsa=" +
loadFile(getFileName(signature, "CurWorkD"));
} else if (headerType == "errorInSignature") {
// this content-signature header is missing the p384ecdsa directive
csHeader = "x5u=" + x5uString + ";p385ecdsa=" +
csHeader = "keyid=" + keyId + ";p385ecdsa=" +
loadFile(getFileName(signature, "CurWorkD"));
}

Просмотреть файл

@ -1,51 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICSTCCATOgAwIBAgIUWQzTTfKLNZgX5ngi/ENiI2DO2kowCwYJKoZIhvcNAQEL
MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
dKpuqc6jRDBCMBMGA1UdJQQMMAoGCCsGAQUFBwMDMCsGA1UdEQQkMCKCIHJlbW90
ZS1uZXd0YWItc2lnbmVyLm1vemlsbGEub3JnMAsGCSqGSIb3DQEBCwOCAQEAc2nE
feYpA8WFyiPfZi56NgVgc8kXSKRNgplDtBHXK7gT7ICNQTSKkt+zHxnS9tAoXoix
OGKsyp/8LNIYGMr4vHVNyOGnxuiLzAYjmDxXhp3t36xOFlU5Y7UaKf9G4feMXrNH
+q1SPYlP84keo1MaC5yhTZTTmJMKkRBsCbIVhfDnL3BUczxVZmk9F+7qK/trL222
RoAaTZW5hdXUZrX630CYs1sQHWgL0B5rg2y9bwFk7toQ34JbjS0Z25e/MZUtFz19
5tSjAZQHlLE6fAYZ3knrxF9xVMJCZf7gQqVphJzBtgy9yvTAtlMsrf6XS6sRRngz
27HBxIpd4tYniYrtfg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC0TCCAbugAwIBAgIULYyr3v/0zZ+XiR22NH7hOcnj2FcwCwYJKoZIhvcNAQEL
MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
MDBaMBExDzANBgNVBAMMBmludC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMlMCMwDAYDVR0TBAUwAwEB/zAT
BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADfRBKSM08JF6vqz
0EA+KNc0XIEAWApuHuwX6XXWeLgo6QN4E/9qfrsaO+C366WT+JDsjDOi40wW46SA
XbguxtZQeZasNDUWp/leZix4RSJoHB7OllG1rgZJfN76zKVaXRGUmyQObkMMOJZe
wIA0OBURT8ik9Z89pD0IWrqscds71Edfjt0hHgg63wVvIaklReZXvFOD3VmSCPNn
2wB6ZzECcbhJpnzxZdsoMSGH0C6apYnNNTjqZjO90JVm/Ph/7nbi/KncYXA6ccl6
Jz2mfiAquWIua2+CzBGbqjZVSATTpWCp+cXQJE1xka+hWUaL5HPTq1bTULRFlauZ
HGl5lJk=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICzTCCAbegAwIBAgIUIVkGGA8HiO3RIKGjdOjVi+d6EVkwCwYJKoZIhvcNAQEL
MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/MBMGA1Ud
JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAlpbRzRIPnf43AwGfMvKP
zOtntRy2nE9GlmY9I00uioHUnUrPLs8aw3UDtyiDWMGqcYysXGx9EX2Vk0POS4gf
G6PA95F6GxTtbzIEZmTPVuzA/cfc9HU3HXDPqh+dySJ8/Ta4c4vX1lgeGGAvstNe
q+9DaCGXs8MqMF8KtXNmOm3eS9q622hKEvTVEoxqj1t365kwKHaNpbObddQ6Xcny
akvfh2L+8QbJSflcm8fL/JTup/2/cRG1ytOsaiXEr9JBEITOtQO0Ot/4Qzq+MJjv
weaJ3hZ0c+cTy3tEvt+I7+lnW4Q5dB7aLR2/BZfLubhxz1SUVMuHfLH64fc0Uf1Q
Nw==
-----END CERTIFICATE-----

Просмотреть файл

@ -1,210 +0,0 @@
/*
* Test Content-Signature for remote about:newtab
* - Bug 1226928 - allow about:newtab to load remote content
*
* This tests content-signature verification on remote about:newtab in the
* following cases (see TESTS, all failed loads display about:blank fallback):
* - good case (signature should verify and correct page is displayed)
* - reload of newtab when the siganture was invalidated after the last correct
* load
* - malformed content-signature header
* - malformed keyid directive
* - malformed p384ecdsa directive
* - wrong signature (this is not a siganture for the delivered document)
* - invalid signature (this is not even a signature)
* - loading a file that doesn't fit the key or signature
* - cache poisoning (load a malicious remote page not in newtab, subsequent
* newtab load has to load the fallback)
*/
const ABOUT_NEWTAB_URI = "about:newtab";
const BASE = "https://example.com/browser/dom/security/test/contentverifier/file_contentserver.sjs?";
const URI_GOOD = BASE + "sig=good&x5u=good&file=good&header=good";
const INVALIDATE_FILE = BASE + "invalidateFile=yep";
const VALIDATE_FILE = BASE + "validateFile=yep";
const URI_HEADER_BASE = BASE + "sig=good&x5u=good&file=good&header=";
const URI_ERROR_HEADER = URI_HEADER_BASE + "error";
const URI_KEYERROR_HEADER = URI_HEADER_BASE + "errorInX5U";
const URI_SIGERROR_HEADER = URI_HEADER_BASE + "errorInSignature";
const URI_NO_HEADER = URI_HEADER_BASE + "noHeader";
const URI_BAD_SIG = BASE + "sig=bad&x5u=good&file=good&header=good";
const URI_BROKEN_SIG = BASE + "sig=broken&x5u=good&file=good&header=good";
const URI_BAD_X5U = BASE + "sig=good&x5u=bad&file=good&header=good";
const URI_HTTP_X5U = BASE + "sig=good&x5u=http&file=good&header=good";
const URI_BAD_FILE = BASE + "sig=good&x5u=good&file=bad&header=good";
const URI_BAD_ALL = BASE + "sig=bad&x5u=bad&file=bad&header=bad";
const URI_BAD_CSP = BASE + "sig=bad-csp&x5u=good&file=bad-csp&header=good";
const URI_BAD_FILE_CACHED = BASE + "sig=good&x5u=good&file=bad&header=good&cached=true";
const GOOD_ABOUT_STRING = "Just a fully good testpage for Bug 1226928";
const BAD_ABOUT_STRING = "Just a bad testpage for Bug 1226928";
const ABOUT_BLANK = "<head></head><body></body>";
const URI_CLEANUP = BASE + "cleanup=true";
const CLEANUP_DONE = "Done";
const URI_SRI = BASE + "sig=sri&x5u=good&file=sri&header=good";
const STYLESHEET_WITHOUT_SRI_BLOCKED = "Stylesheet without SRI blocked";
const STYLESHEET_WITH_SRI_BLOCKED = "Stylesheet with SRI blocked";
const STYLESHEET_WITH_SRI_LOADED = "Stylesheet with SRI loaded";
const SCRIPT_WITHOUT_SRI_BLOCKED = "Script without SRI blocked";
const SCRIPT_WITH_SRI_BLOCKED = "Script with SRI blocked";
const SCRIPT_WITH_SRI_LOADED = "Script with SRI loaded";
const CSP_TEST_SUCCESS_STRING = "CSP violation test succeeded.";
// Needs to sync with pref "security.signed_content.CSP.default".
const SIGNED_CONTENT_CSP = `{"csp-policies":[{"report-only":false,"script-src":["https://example.com","'unsafe-inline'"],"style-src":["https://example.com"]}]}`;
var browser = null;
var aboutNewTabService = Cc["@mozilla.org/browser/aboutnewtab-service;1"]
.getService(Ci.nsIAboutNewTabService);
function pushPrefs(...aPrefs) {
return new Promise((resolve) => {
SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
});
}
/*
* run tests with input from TESTS
*/
function doTest(aExpectedStrings, reload, aUrl, aNewTabPref) {
// set about:newtab location for this test if it's a newtab test
if (aNewTabPref) {
aboutNewTabService.newTabURL = aNewTabPref;
}
// set prefs
yield pushPrefs(
["browser.newtabpage.remote.content-signing-test", true],
["browser.newtabpage.remote", true],
["security.content.signature.root_hash",
"65:AE:D8:1E:B5:12:AE:B0:6B:38:58:BC:7C:47:35:3D:D4:EA:25:F1:63:DA:08:BB:86:3A:2E:97:39:66:8F:55"]);
if (aNewTabPref === URI_BAD_CSP) {
// Use stricter CSP to test CSP violation.
yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self'; style-src 'self'"]);
} else {
// Use weaker CSP to test normal content.
yield pushPrefs(["security.signed_content.CSP.default", "script-src 'self' 'unsafe-inline'; style-src 'self'"]);
}
// start the test
yield BrowserTestUtils.withNewTab({
gBrowser,
url: aUrl,
},
function * (browser) {
// check if everything's set correct for testing
ok(Services.prefs.getBoolPref(
"browser.newtabpage.remote.content-signing-test"),
"sanity check: remote newtab signing test should be used");
ok(Services.prefs.getBoolPref("browser.newtabpage.remote"),
"sanity check: remote newtab should be used");
// we only check this if we really do a newtab test
if (aNewTabPref) {
ok(aboutNewTabService.overridden,
"sanity check: default URL for about:newtab should be overriden");
is(aboutNewTabService.newTabURL, aNewTabPref,
"sanity check: default URL for about:newtab should return the new URL");
}
// Every valid remote newtab page must have built-in CSP.
let shouldHaveCSP = ((aUrl === ABOUT_NEWTAB_URI) &&
(aNewTabPref === URI_GOOD || aNewTabPref === URI_SRI));
if (shouldHaveCSP) {
is(browser.contentDocument.nodePrincipal.cspJSON, SIGNED_CONTENT_CSP,
"Valid remote newtab page must have built-in CSP.");
}
yield ContentTask.spawn(
browser, aExpectedStrings, function * (aExpectedStrings) {
for (let expectedString of aExpectedStrings) {
ok(content.document.documentElement.innerHTML.includes(expectedString),
"Expect the following value in the result\n" + expectedString +
"\nand got " + content.document.documentElement.innerHTML);
}
});
// for good test cases we check if a reload fails if the remote page
// changed from valid to invalid in the meantime
if (reload) {
yield BrowserTestUtils.withNewTab({
gBrowser,
url: INVALIDATE_FILE,
},
function * (browser2) {
yield ContentTask.spawn(browser2, null, function * () {
ok(content.document.documentElement.innerHTML.includes("Done"),
"Expect the following value in the result\n" + "Done" +
"\nand got " + content.document.documentElement.innerHTML);
});
}
);
browser.reload();
yield BrowserTestUtils.browserLoaded(browser);
let expectedStrings = [ABOUT_BLANK];
if (aNewTabPref == URI_SRI) {
expectedStrings = [
STYLESHEET_WITHOUT_SRI_BLOCKED,
STYLESHEET_WITH_SRI_BLOCKED,
SCRIPT_WITHOUT_SRI_BLOCKED,
SCRIPT_WITH_SRI_BLOCKED
];
}
yield ContentTask.spawn(browser, expectedStrings,
function * (expectedStrings) {
for (let expectedString of expectedStrings) {
ok(content.document.documentElement.innerHTML.includes(expectedString),
"Expect the following value in the result\n" + expectedString +
"\nand got " + content.document.documentElement.innerHTML);
}
}
);
yield BrowserTestUtils.withNewTab({
gBrowser,
url: VALIDATE_FILE,
},
function * (browser2) {
yield ContentTask.spawn(browser2, null, function * () {
ok(content.document.documentElement.innerHTML.includes("Done"),
"Expect the following value in the result\n" + "Done" +
"\nand got " + content.document.documentElement.innerHTML);
});
}
);
}
}
);
}
function runTests() {
// run tests from TESTS
for (let i = 0; i < TESTS.length; i++) {
let testCase = TESTS[i];
let url = "", aNewTabPref = "";
let reload = false;
var aExpectedStrings = testCase.testStrings;
if (testCase.aboutURI) {
url = ABOUT_NEWTAB_URI;
aNewTabPref = testCase.aboutURI;
if (aNewTabPref == URI_GOOD || aNewTabPref == URI_SRI) {
reload = true;
}
} else {
url = testCase.url;
}
yield doTest(aExpectedStrings, reload, url, aNewTabPref);
}
}

Просмотреть файл

@ -1131,6 +1131,20 @@ nsHttpChannel::CallOnStartRequest()
}
}
// Check for a Content-Signature header and inject mediator if the header is
// requested and available.
// If requested (mLoadInfo->GetVerifySignedContent), but not present, or
// present but not valid, fail this channel and return
// NS_ERROR_INVALID_SIGNATURE to indicate a signature error and trigger a
// fallback load in nsDocShell.
if (!mCanceled) {
rv = ProcessContentSignatureHeader(mResponseHead);
if (NS_FAILED(rv)) {
LOG(("Content-signature verification failed.\n"));
return rv;
}
}
LOG((" calling mListener->OnStartRequest\n"));
if (mListener) {
MOZ_ASSERT(!mOnStartRequestCalled,
@ -1193,24 +1207,6 @@ nsHttpChannel::CallOnStartRequest()
}
}
// Check for a Content-Signature header and inject mediator if the header is
// requested and available.
// If requested (mLoadInfo->GetVerifySignedContent), but not present, or
// present but not valid, fail this channel and return
// NS_ERROR_INVALID_SIGNATURE to indicate a signature error and trigger a
// fallback load in nsDocShell.
// Note that OnStartRequest has already been called on the target stream
// listener at this point. We have to add the listener here that late to
// ensure that it's the last listener and can thus block the load in
// OnStopRequest.
if (!mCanceled) {
rv = ProcessContentSignatureHeader(mResponseHead);
if (NS_FAILED(rv)) {
LOG(("Content-signature verification failed.\n"));
return rv;
}
}
return NS_OK;
}
@ -1532,8 +1528,8 @@ nsHttpChannel::ProcessContentSignatureHeader(nsHttpResponseHead *aResponseHead)
// create a new listener that meadiates the content
RefPtr<ContentVerifier> contentVerifyingMediator =
new ContentVerifier(mListener, mListenerContext);
rv = contentVerifyingMediator->Init(contentSignatureHeader, this,
mListenerContext);
rv = contentVerifyingMediator->Init(
NS_ConvertUTF8toUTF16(contentSignatureHeader));
NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
mListener = contentVerifyingMediator;

Просмотреть файл

@ -10,15 +10,10 @@
#include "SharedCertVerifier.h"
#include "cryptohi.h"
#include "keyhi.h"
#include "mozilla/Assertions.h"
#include "mozilla/Casting.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsISupportsPriority.h"
#include "nsIURI.h"
#include "nsNSSComponent.h"
#include "nsSecurityHeaderParser.h"
#include "nsStreamUtils.h"
#include "nsWhitespaceTokenizer.h"
#include "nsXPCOMStrings.h"
#include "nssb64.h"
@ -26,10 +21,7 @@
#include "pkix/pkixtypes.h"
#include "secerr.h"
NS_IMPL_ISUPPORTS(ContentSignatureVerifier,
nsIContentSignatureVerifier,
nsIInterfaceRequestor,
nsIStreamListener)
NS_IMPL_ISUPPORTS(ContentSignatureVerifier, nsIContentSignatureVerifier)
using namespace mozilla;
using namespace mozilla::pkix;
@ -134,18 +126,27 @@ ReadChainIntoCertList(const nsACString& aCertChain, CERTCertList* aCertList,
return NS_OK;
}
nsresult
ContentSignatureVerifier::CreateContextInternal(const nsACString& aData,
const nsACString& aCertChain,
const nsACString& aName)
// Create a context for a content signature verification.
// It sets signature, certificate chain and name that should be used to verify
// the data. The data parameter is the first part of the data to verify (this
// can be the empty string).
NS_IMETHODIMP
ContentSignatureVerifier::CreateContext(const nsACString& aData,
const nsACString& aCSHeader,
const nsACString& aCertChain,
const nsACString& aName)
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mMutex);
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
return NS_ERROR_FAILURE;
}
if (mCx) {
return NS_ERROR_ALREADY_INITIALIZED;
}
UniqueCERTCertList certCertList(CERT_NewCertList());
if (!certCertList) {
return NS_ERROR_OUT_OF_MEMORY;
@ -213,6 +214,12 @@ ContentSignatureVerifier::CreateContextInternal(const nsACString& aData,
return NS_ERROR_INVALID_SIGNATURE;
}
// we get the raw content-signature header here, so first parse aCSHeader
rv = ParseContentSignatureHeader(aCSHeader);
if (NS_FAILED(rv)) {
return rv;
}
// Base 64 decode the signature
UniqueSECItem rawSignatureItem(::SECITEM_AllocItem(nullptr, nullptr, 0));
if (!rawSignatureItem ||
@ -254,118 +261,18 @@ ContentSignatureVerifier::CreateContextInternal(const nsACString& aData,
return NS_ERROR_INVALID_SIGNATURE;
}
rv = UpdateInternal(kPREFIX, locker);
rv = UpdateInternal(kPREFIX, lock, locker);
if (NS_FAILED(rv)) {
return rv;
}
// add data if we got any
return UpdateInternal(aData, locker);
return UpdateInternal(aData, lock, locker);
}
nsresult
ContentSignatureVerifier::DownloadCertChain()
{
MOZ_ASSERT(NS_IsMainThread());
if (mCertChainURL.IsEmpty()) {
return NS_ERROR_INVALID_SIGNATURE;
}
nsCOMPtr<nsIURI> certChainURI;
nsresult rv = NS_NewURI(getter_AddRefs(certChainURI), mCertChainURL);
if (NS_FAILED(rv) || !certChainURI) {
return rv;
}
// If the address is not https, fail.
bool isHttps = false;
rv = certChainURI->SchemeIs("https", &isHttps);
if (NS_FAILED(rv)) {
return rv;
}
if (!isHttps) {
return NS_ERROR_INVALID_SIGNATURE;
}
rv = NS_NewChannel(getter_AddRefs(mChannel), certChainURI,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER);
if (NS_FAILED(rv)) {
return rv;
}
// we need this chain soon
nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(mChannel);
if (priorityChannel) {
priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
}
rv = mChannel->AsyncOpen2(this);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
// Create a context for content signature verification using CreateContext below.
// This function doesn't require a cert chain to be passed, but instead aCSHeader
// must contain an x5u value that is then used to download the cert chain.
NS_IMETHODIMP
ContentSignatureVerifier::CreateContextWithoutCertChain(
nsIContentSignatureReceiverCallback *aCallback, const nsACString& aCSHeader,
const nsACString& aName)
{
MOZ_ASSERT(NS_IsMainThread());
if (mInitialised) {
return NS_ERROR_ALREADY_INITIALIZED;
}
mInitialised = true;
// we get the raw content-signature header here, so first parse aCSHeader
nsresult rv = ParseContentSignatureHeader(aCSHeader);
if (NS_FAILED(rv)) {
return rv;
}
mCallback = aCallback;
mName.Assign(aName);
// We must download the cert chain now.
// This is async and blocks createContextInternal calls.
return DownloadCertChain();
}
// Create a context for a content signature verification.
// It sets signature, certificate chain and name that should be used to verify
// the data. The data parameter is the first part of the data to verify (this
// can be the empty string).
NS_IMETHODIMP
ContentSignatureVerifier::CreateContext(const nsACString& aData,
const nsACString& aCSHeader,
const nsACString& aCertChain,
const nsACString& aName)
{
if (mInitialised) {
return NS_ERROR_ALREADY_INITIALIZED;
}
mInitialised = true;
// The cert chain is given in aCertChain so we don't have to download anything.
mHasCertChain = true;
// we get the raw content-signature header here, so first parse aCSHeader
nsresult rv = ParseContentSignatureHeader(aCSHeader);
if (NS_FAILED(rv)) {
return rv;
}
return CreateContextInternal(aData, aCertChain, aName);
}
nsresult
ContentSignatureVerifier::UpdateInternal(
const nsACString& aData, const nsNSSShutDownPreventionLock& /*proofOfLock*/)
ContentSignatureVerifier::UpdateInternal(const nsACString& aData,
MutexAutoLock& /*proofOfLock*/,
const nsNSSShutDownPreventionLock& /*proofOfLock*/)
{
if (!aData.IsEmpty()) {
if (VFY_Update(mCx.get(), (const unsigned char*)nsPromiseFlatCString(aData).get(),
@ -382,22 +289,13 @@ ContentSignatureVerifier::UpdateInternal(
NS_IMETHODIMP
ContentSignatureVerifier::Update(const nsACString& aData)
{
MOZ_ASSERT(NS_IsMainThread());
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
return NS_ERROR_FAILURE;
}
// If we didn't create the context yet, bail!
if (!mHasCertChain) {
MOZ_ASSERT_UNREACHABLE(
"Someone called ContentSignatureVerifier::Update before "
"downloading the cert chain.");
return NS_ERROR_FAILURE;
}
return UpdateInternal(aData, locker);
MutexAutoLock lock(mMutex);
return UpdateInternal(aData, lock, locker);
}
/**
@ -407,21 +305,13 @@ NS_IMETHODIMP
ContentSignatureVerifier::End(bool* _retval)
{
NS_ENSURE_ARG(_retval);
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mMutex);
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
return NS_ERROR_FAILURE;
}
// If we didn't create the context yet, bail!
if (!mHasCertChain) {
MOZ_ASSERT_UNREACHABLE(
"Someone called ContentSignatureVerifier::End before "
"downloading the cert chain.");
return NS_ERROR_FAILURE;
}
*_retval = (VFY_End(mCx.get()) == SECSuccess);
return NS_OK;
@ -431,10 +321,8 @@ nsresult
ContentSignatureVerifier::ParseContentSignatureHeader(
const nsACString& aContentSignatureHeader)
{
MOZ_ASSERT(NS_IsMainThread());
// We only support p384 ecdsa according to spec
NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
NS_NAMED_LITERAL_CSTRING(certChainURL_var, "x5u");
nsSecurityHeaderParser parser(aContentSignatureHeader.BeginReading());
nsresult rv = parser.Parse();
@ -458,17 +346,6 @@ ContentSignatureVerifier::ParseContentSignatureHeader(
CSVerifier_LOG(("CSVerifier: found a ContentSignature directive\n"));
mSignature = directive->mValue;
}
if (directive->mName.Length() == certChainURL_var.Length() &&
directive->mName.EqualsIgnoreCase(certChainURL_var.get(),
certChainURL_var.Length())) {
if (!mCertChainURL.IsEmpty()) {
CSVerifier_LOG(("CSVerifier: found two x5u values\n"));
return NS_ERROR_INVALID_SIGNATURE;
}
CSVerifier_LOG(("CSVerifier: found an x5u directive\n"));
mCertChainURL = directive->mValue;
}
}
// we have to ensure that we found a signature at this point
@ -485,81 +362,3 @@ ContentSignatureVerifier::ParseContentSignatureHeader(
return NS_OK;
}
/* nsIStreamListener implementation */
NS_IMETHODIMP
ContentSignatureVerifier::OnStartRequest(nsIRequest* aRequest,
nsISupports* aContext)
{
MOZ_ASSERT(NS_IsMainThread());
return NS_OK;
}
NS_IMETHODIMP
ContentSignatureVerifier::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext, nsresult aStatus)
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIContentSignatureReceiverCallback> callback;
callback.swap(mCallback);
nsresult rv;
// Check HTTP status code and return if it's not 200.
nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest, &rv);
uint32_t httpResponseCode;
if (NS_FAILED(rv) || NS_FAILED(http->GetResponseStatus(&httpResponseCode)) ||
httpResponseCode != 200) {
callback->ContextCreated(false);
return NS_OK;
}
if (NS_FAILED(aStatus)) {
callback->ContextCreated(false);
return NS_OK;
}
nsAutoCString certChain;
for (uint32_t i = 0; i < mCertChain.Length(); ++i) {
certChain.Append(mCertChain[i]);
}
// We got the cert chain now. Let's create the context.
rv = CreateContextInternal(NS_LITERAL_CSTRING(""), certChain, mName);
if (NS_FAILED(rv)) {
callback->ContextCreated(false);
return NS_OK;
}
mHasCertChain = true;
callback->ContextCreated(true);
return NS_OK;
}
NS_IMETHODIMP
ContentSignatureVerifier::OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aInputStream,
uint64_t aOffset, uint32_t aCount)
{
MOZ_ASSERT(NS_IsMainThread());
nsAutoCString buffer;
nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
if (NS_FAILED(rv)) {
return rv;
}
if (!mCertChain.AppendElement(buffer, fallible)) {
mCertChain.TruncateLength(0);
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
NS_IMETHODIMP
ContentSignatureVerifier::GetInterface(const nsIID& uuid, void** result)
{
return QueryInterface(uuid, result);
}

Просмотреть файл

@ -11,7 +11,6 @@
#include "cert.h"
#include "CSTrustDomain.h"
#include "nsIContentSignatureVerifier.h"
#include "nsIStreamListener.h"
#include "nsNSSShutDown.h"
#include "ScopedNSSTypes.h"
@ -23,21 +22,15 @@
"@mozilla.org/security/contentsignatureverifier;1"
class ContentSignatureVerifier final : public nsIContentSignatureVerifier
, public nsIStreamListener
, public nsNSSShutDownObject
, public nsIInterfaceRequestor
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTSIGNATUREVERIFIER
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
ContentSignatureVerifier()
: mCx(nullptr)
, mInitialised(false)
, mHasCertChain(false)
, mMutex("CSVerifier::mMutex")
{
}
@ -51,11 +44,8 @@ private:
~ContentSignatureVerifier();
nsresult UpdateInternal(const nsACString& aData,
MutexAutoLock& /*proofOfLock*/,
const nsNSSShutDownPreventionLock& /*proofOfLock*/);
nsresult DownloadCertChain();
nsresult CreateContextInternal(const nsACString& aData,
const nsACString& aCertChain,
const nsACString& aName);
void destructorSafeDestroyNSSReference()
{
@ -67,26 +57,11 @@ private:
// verifier context for incremental verifications
mozilla::UniqueVFYContext mCx;
bool mInitialised;
// Indicates whether we hold a cert chain to verify the signature or not.
// It's set by default in CreateContext or when the channel created in
// DownloadCertChain finished. Update and End must only be called after
// mHashCertChain is set.
bool mHasCertChain;
// signature to verify
nsCString mSignature;
// x5u (X.509 URL) value pointing to pem cert chain
nsCString mCertChainURL;
// the downloaded cert chain to verify against
FallibleTArray<nsCString> mCertChain;
// verification key
mozilla::UniqueSECKEYPublicKey mKey;
// name of the verifying context
nsCString mName;
// callback to notify when finished
nsCOMPtr<nsIContentSignatureReceiverCallback> mCallback;
// channel to download the cert chain
nsCOMPtr<nsIChannel> mChannel;
mozilla::Mutex mMutex;
};
#endif // ContentSignatureVerifier_h

Просмотреть файл

@ -5,8 +5,6 @@
#include "nsISupports.idl"
interface nsIContentSignatureReceiverCallback;
/**
* An interface for verifying content-signatures, inspired by
* https://tools.ietf.org/html/draft-thomson-http-content-signature-00
@ -40,8 +38,7 @@ interface nsIContentSignatureVerifier : nsISupports
* @returns true if the signature matches the data and aCertificateChain is
* valid within aContext, false if not.
*/
boolean verifyContentSignature(in ACString aData,
in ACString aContentSignatureHeader,
boolean verifyContentSignature(in ACString aData, in ACString aSignature,
in ACString aCertificateChain,
in ACString aName);
@ -57,30 +54,9 @@ interface nsIContentSignatureVerifier : nsISupports
* @param aName The (host)name for which the end entity must
be valid.
*/
void createContext(in ACString aData, in ACString aContentSignatureHeader,
void createContext(in ACString aData, in ACString aSignature,
in ACString aCertificateChain, in ACString aName);
/**
* Creates a context to verify a content signature against data that is added
* later with update calls.
* This does not require the caller to download the certificate chain. It's
* done internally.
* It requires the x5u parameter to be present in aContentSignatureHeader
*
* NOTE: Callers have to wait for aCallback to return before invoking anything
* else. Otherwise the ContentSignatureVerifier will fail.
*
* @param aCallback Callback that's invoked when the cert chain
* got fetched.
* @param aContentSignatureHeader The signature of the data, url-safe base64
* encoded, and the x5u value.
* @param aName The (host)name for which the end entity must
be valid.
*/
void createContextWithoutCertChain(in nsIContentSignatureReceiverCallback aCallback,
in ACString aContentSignatureHeader,
in ACString aName);
/**
* Adds data to the context that was used to generate the signature.
*
@ -96,21 +72,5 @@ interface nsIContentSignatureVerifier : nsISupports
* and update, false if not.
*/
boolean end();
};
/**
* Callback for nsIContentSignatureVerifier.
* { 0x1eb90707, 0xdf59, 0x48b7, \
* { 0x9d, 0x42, 0xd8, 0xbf, 0x63, 0x0a, 0xe7, 0x44 } }
*/
[scriptable, uuid(1eb90707-df59-48b7-9d42-d8bf630ae744)]
interface nsIContentSignatureReceiverCallback : nsISupports
{
/**
* Notification callback that's called by nsIContentSignatureVerifier when
* the cert chain is downloaded.
* If download and initialisation were successful, successful is true,
* otherwise false. If successful is false, the verification must be aborted.
*/
void contextCreated(in boolean successful);
};