SRICheckDataVerifier::SRICheckDataVerifier(const SRIMetadata& aMetadata, const nsACString& aSourceFileURI, nsIConsoleReportCollector* aReporter) : mCryptoHash(nullptr) , mBytesHashed(0) , mHashLength(0) , mHashType('\0') , mInvalidMetadata(false) , mComplete(false) { MOZ_ASSERT(!aMetadata.IsEmpty()); // should be checked by caller // IntegrityMetadata() checks this and returns "no metadata" if // it's disabled so we should never make it this far MOZ_ASSERT(Preferences::GetBool("security.sri.enable", false)); MOZ_ASSERT(aReporter); if (!aMetadata.IsValid()) { nsTArray<nsString> params; aReporter->AddConsoleReport(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), nsContentUtils::eSECURITY_PROPERTIES, aSourceFileURI, 0, 0, NS_LITERAL_CSTRING("NoValidMetadata"), const_cast<const nsTArray<nsString>&>(params)); mInvalidMetadata = true; return; // ignore invalid metadata for forward-compatibility } aMetadata.GetHashType(&mHashType, &mHashLength); }
SRICheckDataVerifier::SRICheckDataVerifier(const SRIMetadata& aMetadata, const nsIDocument* aDocument) : mCryptoHash(nullptr), mBytesHashed(0), mInvalidMetadata(false), mComplete(false) { MOZ_ASSERT(!aMetadata.IsEmpty()); // should be checked by caller // IntegrityMetadata() checks this and returns "no metadata" if // it's disabled so we should never make it this far MOZ_ASSERT(Preferences::GetBool("security.sri.enable", false)); if (!aMetadata.IsValid()) { nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), aDocument, nsContentUtils::eSECURITY_PROPERTIES, "NoValidMetadata"); mInvalidMetadata = true; return; // ignore invalid metadata for forward-compatibility } uint32_t hashLength; aMetadata.GetHashType(&mHashType, &hashLength); }
nsresult SRICheckDataVerifier::VerifyHash(const SRIMetadata& aMetadata, uint32_t aHashIndex, const nsACString& aSourceFileURI, nsIConsoleReportCollector* aReporter) { NS_ENSURE_ARG_POINTER(aReporter); nsAutoCString base64Hash; aMetadata.GetHash(aHashIndex, &base64Hash); SRILOG(("SRICheckDataVerifier::VerifyHash, hash[%u]=%s", aHashIndex, base64Hash.get())); nsAutoCString binaryHash; if (NS_WARN_IF(NS_FAILED(Base64Decode(base64Hash, binaryHash)))) { nsTArray<nsString> params; aReporter->AddConsoleReport(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), nsContentUtils::eSECURITY_PROPERTIES, aSourceFileURI, 0, 0, NS_LITERAL_CSTRING("InvalidIntegrityBase64"), const_cast<const nsTArray<nsString>&>(params)); return NS_ERROR_SRI_CORRUPT; } uint32_t hashLength; int8_t hashType; aMetadata.GetHashType(&hashType, &hashLength); if (binaryHash.Length() != hashLength) { nsTArray<nsString> params; aReporter->AddConsoleReport(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), nsContentUtils::eSECURITY_PROPERTIES, aSourceFileURI, 0, 0, NS_LITERAL_CSTRING("InvalidIntegrityLength"), const_cast<const nsTArray<nsString>&>(params)); return NS_ERROR_SRI_CORRUPT; } if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) { nsAutoCString encodedHash; nsresult rv = Base64Encode(mComputedHash, encodedHash); if (NS_SUCCEEDED(rv)) { SRILOG(("SRICheckDataVerifier::VerifyHash, mComputedHash=%s", encodedHash.get())); } } if (!binaryHash.Equals(mComputedHash)) { SRILOG(("SRICheckDataVerifier::VerifyHash, hash[%u] did not match", aHashIndex)); return NS_ERROR_SRI_CORRUPT; } SRILOG(("SRICheckDataVerifier::VerifyHash, hash[%u] verified successfully", aHashIndex)); return NS_OK; }
/** * Compute the hash of a sub-resource and compare it with the expected * value. */ static nsresult VerifyHash(const SRIMetadata& aMetadata, uint32_t aHashIndex, uint32_t aStringLen, const uint8_t* aString, const nsIDocument* aDocument) { NS_ENSURE_ARG_POINTER(aString); NS_ENSURE_ARG_POINTER(aDocument); nsAutoCString base64Hash; aMetadata.GetHash(aHashIndex, &base64Hash); SRILOG(("SRICheck::VerifyHash, hash[%u]=%s", aHashIndex, base64Hash.get())); nsAutoCString binaryHash; if (NS_WARN_IF(NS_FAILED(Base64Decode(base64Hash, binaryHash)))) { nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), aDocument, nsContentUtils::eSECURITY_PROPERTIES, "InvalidIntegrityBase64"); return NS_ERROR_SRI_CORRUPT; } uint32_t hashLength; int8_t hashType; aMetadata.GetHashType(&hashType, &hashLength); if (binaryHash.Length() != hashLength) { nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), aDocument, nsContentUtils::eSECURITY_PROPERTIES, "InvalidIntegrityLength"); return NS_ERROR_SRI_CORRUPT; } nsresult rv; nsCOMPtr<nsICryptoHash> cryptoHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv); NS_ENSURE_SUCCESS(rv, rv); rv = cryptoHash->Init(hashType); NS_ENSURE_SUCCESS(rv, rv); rv = cryptoHash->Update(aString, aStringLen); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString computedHash; rv = cryptoHash->Finish(false, computedHash); NS_ENSURE_SUCCESS(rv, rv); if (!binaryHash.Equals(computedHash)) { SRILOG(("SRICheck::VerifyHash, hash[%u] did not match", aHashIndex)); return NS_ERROR_SRI_CORRUPT; } SRILOG(("SRICheck::VerifyHash, hash[%u] verified successfully", aHashIndex)); return NS_OK; }
/* static */ nsresult SRICheck::VerifyIntegrity(const SRIMetadata& aMetadata, nsIURI* aRequestURI, const CORSMode aCORSMode, uint32_t aStringLen, const uint8_t* aString, const nsIDocument* aDocument) { if (MOZ_LOG_TEST(GetSriLog(), mozilla::LogLevel::Debug)) { nsAutoCString requestURL; aRequestURI->GetAsciiSpec(requestURL); // requestURL will be empty if GetAsciiSpec fails SRILOG(("SRICheck::VerifyIntegrity, url=%s (length=%u)", requestURL.get(), aStringLen)); } MOZ_ASSERT(!aMetadata.IsEmpty()); // should be checked by caller // IntegrityMetadata() checks this and returns "no metadata" if // it's disabled so we should never make it this far MOZ_ASSERT(Preferences::GetBool("security.sri.enable", false)); if (NS_FAILED(IsEligible(aRequestURI, aCORSMode, aDocument))) { return NS_OK; // ignore non-CORS resources for forward-compatibility } if (!aMetadata.IsValid()) { nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), aDocument, nsContentUtils::eSECURITY_PROPERTIES, "NoValidMetadata"); return NS_OK; // ignore invalid metadata for forward-compatibility } for (uint32_t i = 0; i < aMetadata.HashCount(); i++) { if (NS_SUCCEEDED(VerifyHash(aMetadata, i, aStringLen, aString, aDocument))) { return NS_OK; // stop at the first valid hash } } nsAutoCString alg; aMetadata.GetAlgorithm(&alg); NS_ConvertUTF8toUTF16 algUTF16(alg); const char16_t* params[] = { algUTF16.get() }; nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), aDocument, nsContentUtils::eSECURITY_PROPERTIES, "IntegrityMismatch", params, ArrayLength(params)); return NS_ERROR_SRI_CORRUPT; }
nsresult SRICheckDataVerifier::Verify(const SRIMetadata& aMetadata, nsIChannel* aChannel, const nsACString& aSourceFileURI, nsIConsoleReportCollector* aReporter) { NS_ENSURE_ARG_POINTER(aReporter); if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) { nsAutoCString requestURL; nsCOMPtr<nsIRequest> request = aChannel; request->GetName(requestURL); SRILOG(("SRICheckDataVerifier::Verify, url=%s (length=%zu)", requestURL.get(), mBytesHashed)); } nsresult rv = Finish(); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE); LoadTainting tainting = loadInfo->GetTainting(); if (NS_FAILED(IsEligible(aChannel, tainting, aSourceFileURI, aReporter))) { return NS_ERROR_SRI_NOT_ELIGIBLE; } if (mInvalidMetadata) { return NS_OK; // ignore invalid metadata for forward-compatibility } for (uint32_t i = 0; i < aMetadata.HashCount(); i++) { if (NS_SUCCEEDED(VerifyHash(aMetadata, i, aSourceFileURI, aReporter))) { return NS_OK; // stop at the first valid hash } } nsAutoCString alg; aMetadata.GetAlgorithm(&alg); NS_ConvertUTF8toUTF16 algUTF16(alg); nsTArray<nsString> params; params.AppendElement(algUTF16); aReporter->AddConsoleReport(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), nsContentUtils::eSECURITY_PROPERTIES, aSourceFileURI, 0, 0, NS_LITERAL_CSTRING("IntegrityMismatch"), const_cast<const nsTArray<nsString>&>(params)); return NS_ERROR_SRI_CORRUPT; }
nsresult SRICheckDataVerifier::Verify(const SRIMetadata& aMetadata, nsIChannel* aChannel, const CORSMode aCORSMode, const nsIDocument* aDocument) { NS_ENSURE_ARG_POINTER(aDocument); if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) { nsAutoCString requestURL; nsCOMPtr<nsIRequest> request; request = do_QueryInterface(aChannel); request->GetName(requestURL); SRILOG(("SRICheckDataVerifier::Verify, url=%s (length=%lu)", requestURL.get(), mBytesHashed)); } nsresult rv = Finish(); NS_ENSURE_SUCCESS(rv, rv); if (NS_FAILED(IsEligible(aChannel, aCORSMode, aDocument))) { return NS_ERROR_SRI_NOT_ELIGIBLE; } if (mInvalidMetadata) { return NS_OK; // ignore invalid metadata for forward-compatibility } for (uint32_t i = 0; i < aMetadata.HashCount(); i++) { if (NS_SUCCEEDED(VerifyHash(aMetadata, i, aDocument))) { return NS_OK; // stop at the first valid hash } } nsAutoCString alg; aMetadata.GetAlgorithm(&alg); NS_ConvertUTF8toUTF16 algUTF16(alg); const char16_t* params[] = { algUTF16.get() }; nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), aDocument, nsContentUtils::eSECURITY_PROPERTIES, "IntegrityMismatch", params, ArrayLength(params)); return NS_ERROR_SRI_CORRUPT; }