tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SRIMetadata.cpp (6291B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "SRIMetadata.h"
      8 
      9 #include "hasht.h"
     10 #include "mozilla/Logging.h"
     11 #include "nsICryptoHash.h"
     12 
     13 static mozilla::LogModule* GetSriMetadataLog() {
     14  static mozilla::LazyLogModule gSriMetadataPRLog("SRIMetadata");
     15  return gSriMetadataPRLog;
     16 }
     17 
     18 #define SRIMETADATALOG(args) \
     19  MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Debug, args)
     20 #define SRIMETADATAERROR(args) \
     21  MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Error, args)
     22 
     23 namespace mozilla::dom {
     24 
     25 SRIMetadata::SRIMetadata(const nsACString& aToken)
     26    : mAlgorithmType(SRIMetadata::UNKNOWN_ALGORITHM), mEmpty(false) {
     27  MOZ_ASSERT(!aToken.IsEmpty());  // callers should check this first
     28 
     29  SRIMETADATALOG(("SRIMetadata::SRIMetadata, aToken='%s'",
     30                  PromiseFlatCString(aToken).get()));
     31 
     32  int32_t hyphen = aToken.FindChar('-');
     33  if (hyphen == -1) {
     34    SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (no hyphen)"));
     35    return;  // invalid metadata
     36  }
     37 
     38  // split the token into its components
     39  mAlgorithm = Substring(aToken, 0, hyphen);
     40  uint32_t hashStart = hyphen + 1;
     41  if (hashStart >= aToken.Length()) {
     42    SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (missing digest)"));
     43    return;  // invalid metadata
     44  }
     45  int32_t question = aToken.FindChar('?');
     46  if (question == -1) {
     47    mHashes.AppendElement(
     48        Substring(aToken, hashStart, aToken.Length() - hashStart));
     49  } else {
     50    MOZ_ASSERT(question > 0);
     51    if (static_cast<uint32_t>(question) <= hashStart) {
     52      SRIMETADATAERROR(
     53          ("SRIMetadata::SRIMetadata, invalid (options w/o digest)"));
     54      return;  // invalid metadata
     55    }
     56    mHashes.AppendElement(Substring(aToken, hashStart, question - hashStart));
     57  }
     58 
     59  if (mAlgorithm.EqualsLiteral("sha256")) {
     60    mAlgorithmType = nsICryptoHash::SHA256;
     61  } else if (mAlgorithm.EqualsLiteral("sha384")) {
     62    mAlgorithmType = nsICryptoHash::SHA384;
     63  } else if (mAlgorithm.EqualsLiteral("sha512")) {
     64    mAlgorithmType = nsICryptoHash::SHA512;
     65  }
     66 
     67  SRIMETADATALOG(("SRIMetadata::SRIMetadata, hash='%s'; alg='%s'",
     68                  mHashes[0].get(), mAlgorithm.get()));
     69 }
     70 
     71 bool SRIMetadata::operator<(const SRIMetadata& aOther) const {
     72  static_assert(nsICryptoHash::SHA256 < nsICryptoHash::SHA384,
     73                "We rely on the order indicating relative alg strength");
     74  static_assert(nsICryptoHash::SHA384 < nsICryptoHash::SHA512,
     75                "We rely on the order indicating relative alg strength");
     76  MOZ_ASSERT(mAlgorithmType == SRIMetadata::UNKNOWN_ALGORITHM ||
     77             mAlgorithmType == nsICryptoHash::SHA256 ||
     78             mAlgorithmType == nsICryptoHash::SHA384 ||
     79             mAlgorithmType == nsICryptoHash::SHA512);
     80  MOZ_ASSERT(aOther.mAlgorithmType == SRIMetadata::UNKNOWN_ALGORITHM ||
     81             aOther.mAlgorithmType == nsICryptoHash::SHA256 ||
     82             aOther.mAlgorithmType == nsICryptoHash::SHA384 ||
     83             aOther.mAlgorithmType == nsICryptoHash::SHA512);
     84 
     85  if (mEmpty) {
     86    SRIMETADATALOG(("SRIMetadata::operator<, first metadata is empty"));
     87    return true;  // anything beats the empty metadata (incl. invalid ones)
     88  }
     89 
     90  SRIMETADATALOG(("SRIMetadata::operator<, alg1='%d'; alg2='%d'",
     91                  mAlgorithmType, aOther.mAlgorithmType));
     92  return (mAlgorithmType < aOther.mAlgorithmType);
     93 }
     94 
     95 bool SRIMetadata::operator>(const SRIMetadata& aOther) const {
     96  MOZ_ASSERT(false);
     97  return false;
     98 }
     99 
    100 SRIMetadata& SRIMetadata::operator+=(const SRIMetadata& aOther) {
    101  MOZ_ASSERT(!aOther.IsEmpty() && !IsEmpty());
    102  MOZ_ASSERT(aOther.IsValid() && IsValid());
    103  MOZ_ASSERT(mAlgorithmType == aOther.mAlgorithmType);
    104 
    105  // We only pull in the first element of the other metadata
    106  MOZ_ASSERT(aOther.mHashes.Length() == 1);
    107  if (mHashes.Length() < SRIMetadata::MAX_ALTERNATE_HASHES) {
    108    SRIMETADATALOG((
    109        "SRIMetadata::operator+=, appending another '%s' hash (new length=%zu)",
    110        mAlgorithm.get(), mHashes.Length()));
    111    mHashes.AppendElement(aOther.mHashes[0]);
    112  }
    113 
    114  MOZ_ASSERT(mHashes.Length() > 1);
    115  MOZ_ASSERT(mHashes.Length() <= SRIMetadata::MAX_ALTERNATE_HASHES);
    116  return *this;
    117 }
    118 
    119 bool SRIMetadata::operator==(const SRIMetadata& aOther) const {
    120  if (IsEmpty() || !IsValid()) {
    121    return false;
    122  }
    123  return mAlgorithmType == aOther.mAlgorithmType;
    124 }
    125 
    126 void SRIMetadata::GetHash(uint32_t aIndex, nsCString* outHash) const {
    127  MOZ_ASSERT(aIndex < SRIMetadata::MAX_ALTERNATE_HASHES);
    128  if (NS_WARN_IF(aIndex >= mHashes.Length())) {
    129    *outHash = nullptr;
    130    return;
    131  }
    132  *outHash = mHashes[aIndex];
    133 }
    134 
    135 void SRIMetadata::GetHashType(int8_t* outType, uint32_t* outLength) const {
    136  // these constants are defined in security/nss/lib/util/hasht.h and
    137  // netwerk/base/public/nsICryptoHash.idl
    138  switch (mAlgorithmType) {
    139    case nsICryptoHash::SHA256:
    140      *outLength = SHA256_LENGTH;
    141      break;
    142    case nsICryptoHash::SHA384:
    143      *outLength = SHA384_LENGTH;
    144      break;
    145    case nsICryptoHash::SHA512:
    146      *outLength = SHA512_LENGTH;
    147      break;
    148    default:
    149      *outLength = 0;
    150  }
    151  *outType = mAlgorithmType;
    152 }
    153 
    154 bool SRIMetadata::CanTrustBeDelegatedTo(const SRIMetadata& aOther) const {
    155  if (IsEmpty()) {
    156    // No integrity requirements enforced, just let go.
    157    return true;
    158  }
    159 
    160  if (aOther.IsEmpty()) {
    161    // This metadata requires a check and the other has none, can't delegate.
    162    return false;
    163  }
    164 
    165  if (mAlgorithmType != aOther.mAlgorithmType) {
    166    // They must use the same hash algorithm.
    167    return false;
    168  }
    169 
    170  // They must be completely identical, except for the order of hashes.
    171  // We don't know which hash is the one passing eventually the check, so only
    172  // option is to require this metadata to contain the same set of hashes as the
    173  // one we want to delegate the trust to.
    174  if (mHashes.Length() != aOther.mHashes.Length()) {
    175    return false;
    176  }
    177 
    178  for (const auto& hash : mHashes) {
    179    if (!aOther.mHashes.Contains(hash)) {
    180      return false;
    181    }
    182  }
    183 
    184  return true;
    185 }
    186 
    187 }  // namespace mozilla::dom