summaryrefslogtreecommitdiffstats
path: root/dom/security/SRIMetadata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/security/SRIMetadata.cpp')
-rw-r--r--dom/security/SRIMetadata.cpp187
1 files changed, 187 insertions, 0 deletions
diff --git a/dom/security/SRIMetadata.cpp b/dom/security/SRIMetadata.cpp
new file mode 100644
index 0000000000..02144f0f13
--- /dev/null
+++ b/dom/security/SRIMetadata.cpp
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SRIMetadata.h"
+
+#include "hasht.h"
+#include "mozilla/Logging.h"
+#include "nsICryptoHash.h"
+
+static mozilla::LogModule* GetSriMetadataLog() {
+ static mozilla::LazyLogModule gSriMetadataPRLog("SRIMetadata");
+ return gSriMetadataPRLog;
+}
+
+#define SRIMETADATALOG(args) \
+ MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Debug, args)
+#define SRIMETADATAERROR(args) \
+ MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Error, args)
+
+namespace mozilla::dom {
+
+SRIMetadata::SRIMetadata(const nsACString& aToken)
+ : mAlgorithmType(SRIMetadata::UNKNOWN_ALGORITHM), mEmpty(false) {
+ MOZ_ASSERT(!aToken.IsEmpty()); // callers should check this first
+
+ SRIMETADATALOG(("SRIMetadata::SRIMetadata, aToken='%s'",
+ PromiseFlatCString(aToken).get()));
+
+ int32_t hyphen = aToken.FindChar('-');
+ if (hyphen == -1) {
+ SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (no hyphen)"));
+ return; // invalid metadata
+ }
+
+ // split the token into its components
+ mAlgorithm = Substring(aToken, 0, hyphen);
+ uint32_t hashStart = hyphen + 1;
+ if (hashStart >= aToken.Length()) {
+ SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (missing digest)"));
+ return; // invalid metadata
+ }
+ int32_t question = aToken.FindChar('?');
+ if (question == -1) {
+ mHashes.AppendElement(
+ Substring(aToken, hashStart, aToken.Length() - hashStart));
+ } else {
+ MOZ_ASSERT(question > 0);
+ if (static_cast<uint32_t>(question) <= hashStart) {
+ SRIMETADATAERROR(
+ ("SRIMetadata::SRIMetadata, invalid (options w/o digest)"));
+ return; // invalid metadata
+ }
+ mHashes.AppendElement(Substring(aToken, hashStart, question - hashStart));
+ }
+
+ if (mAlgorithm.EqualsLiteral("sha256")) {
+ mAlgorithmType = nsICryptoHash::SHA256;
+ } else if (mAlgorithm.EqualsLiteral("sha384")) {
+ mAlgorithmType = nsICryptoHash::SHA384;
+ } else if (mAlgorithm.EqualsLiteral("sha512")) {
+ mAlgorithmType = nsICryptoHash::SHA512;
+ }
+
+ SRIMETADATALOG(("SRIMetadata::SRIMetadata, hash='%s'; alg='%s'",
+ mHashes[0].get(), mAlgorithm.get()));
+}
+
+bool SRIMetadata::operator<(const SRIMetadata& aOther) const {
+ static_assert(nsICryptoHash::SHA256 < nsICryptoHash::SHA384,
+ "We rely on the order indicating relative alg strength");
+ static_assert(nsICryptoHash::SHA384 < nsICryptoHash::SHA512,
+ "We rely on the order indicating relative alg strength");
+ MOZ_ASSERT(mAlgorithmType == SRIMetadata::UNKNOWN_ALGORITHM ||
+ mAlgorithmType == nsICryptoHash::SHA256 ||
+ mAlgorithmType == nsICryptoHash::SHA384 ||
+ mAlgorithmType == nsICryptoHash::SHA512);
+ MOZ_ASSERT(aOther.mAlgorithmType == SRIMetadata::UNKNOWN_ALGORITHM ||
+ aOther.mAlgorithmType == nsICryptoHash::SHA256 ||
+ aOther.mAlgorithmType == nsICryptoHash::SHA384 ||
+ aOther.mAlgorithmType == nsICryptoHash::SHA512);
+
+ if (mEmpty) {
+ SRIMETADATALOG(("SRIMetadata::operator<, first metadata is empty"));
+ return true; // anything beats the empty metadata (incl. invalid ones)
+ }
+
+ SRIMETADATALOG(("SRIMetadata::operator<, alg1='%d'; alg2='%d'",
+ mAlgorithmType, aOther.mAlgorithmType));
+ return (mAlgorithmType < aOther.mAlgorithmType);
+}
+
+bool SRIMetadata::operator>(const SRIMetadata& aOther) const {
+ MOZ_ASSERT(false);
+ return false;
+}
+
+SRIMetadata& SRIMetadata::operator+=(const SRIMetadata& aOther) {
+ MOZ_ASSERT(!aOther.IsEmpty() && !IsEmpty());
+ MOZ_ASSERT(aOther.IsValid() && IsValid());
+ MOZ_ASSERT(mAlgorithmType == aOther.mAlgorithmType);
+
+ // We only pull in the first element of the other metadata
+ MOZ_ASSERT(aOther.mHashes.Length() == 1);
+ if (mHashes.Length() < SRIMetadata::MAX_ALTERNATE_HASHES) {
+ SRIMETADATALOG((
+ "SRIMetadata::operator+=, appending another '%s' hash (new length=%zu)",
+ mAlgorithm.get(), mHashes.Length()));
+ mHashes.AppendElement(aOther.mHashes[0]);
+ }
+
+ MOZ_ASSERT(mHashes.Length() > 1);
+ MOZ_ASSERT(mHashes.Length() <= SRIMetadata::MAX_ALTERNATE_HASHES);
+ return *this;
+}
+
+bool SRIMetadata::operator==(const SRIMetadata& aOther) const {
+ if (IsEmpty() || !IsValid()) {
+ return false;
+ }
+ return mAlgorithmType == aOther.mAlgorithmType;
+}
+
+void SRIMetadata::GetHash(uint32_t aIndex, nsCString* outHash) const {
+ MOZ_ASSERT(aIndex < SRIMetadata::MAX_ALTERNATE_HASHES);
+ if (NS_WARN_IF(aIndex >= mHashes.Length())) {
+ *outHash = nullptr;
+ return;
+ }
+ *outHash = mHashes[aIndex];
+}
+
+void SRIMetadata::GetHashType(int8_t* outType, uint32_t* outLength) const {
+ // these constants are defined in security/nss/lib/util/hasht.h and
+ // netwerk/base/public/nsICryptoHash.idl
+ switch (mAlgorithmType) {
+ case nsICryptoHash::SHA256:
+ *outLength = SHA256_LENGTH;
+ break;
+ case nsICryptoHash::SHA384:
+ *outLength = SHA384_LENGTH;
+ break;
+ case nsICryptoHash::SHA512:
+ *outLength = SHA512_LENGTH;
+ break;
+ default:
+ *outLength = 0;
+ }
+ *outType = mAlgorithmType;
+}
+
+bool SRIMetadata::CanTrustBeDelegatedTo(const SRIMetadata& aOther) const {
+ if (IsEmpty()) {
+ // No integrity requirements enforced, just let go.
+ return true;
+ }
+
+ if (aOther.IsEmpty()) {
+ // This metadata requires a check and the other has none, can't delegate.
+ return false;
+ }
+
+ if (mAlgorithmType != aOther.mAlgorithmType) {
+ // They must use the same hash algorithm.
+ return false;
+ }
+
+ // They must be completely identical, except for the order of hashes.
+ // We don't know which hash is the one passing eventually the check, so only
+ // option is to require this metadata to contain the same set of hashes as the
+ // one we want to delegate the trust to.
+ if (mHashes.Length() != aOther.mHashes.Length()) {
+ return false;
+ }
+
+ for (const auto& hash : mHashes) {
+ if (!aOther.mHashes.Contains(hash)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace mozilla::dom