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