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/. */
7 #include "SRIMetadata.h"
10 #include "mozilla/Logging.h"
11 #include "nsICryptoHash.h"
13 static mozilla::LogModule
* GetSriMetadataLog() {
14 static mozilla::LazyLogModule
gSriMetadataPRLog("SRIMetadata");
15 return gSriMetadataPRLog
;
18 #define SRIMETADATALOG(args) \
19 MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Debug, args)
20 #define SRIMETADATAERROR(args) \
21 MOZ_LOG(GetSriMetadataLog(), mozilla::LogLevel::Error, args)
23 namespace mozilla::dom
{
25 SRIMetadata::SRIMetadata(const nsACString
& aToken
)
26 : mAlgorithmType(SRIMetadata::UNKNOWN_ALGORITHM
), mEmpty(false) {
27 MOZ_ASSERT(!aToken
.IsEmpty()); // callers should check this first
29 SRIMETADATALOG(("SRIMetadata::SRIMetadata, aToken='%s'",
30 PromiseFlatCString(aToken
).get()));
32 int32_t hyphen
= aToken
.FindChar('-');
34 SRIMETADATAERROR(("SRIMetadata::SRIMetadata, invalid (no hyphen)"));
35 return; // invalid metadata
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
45 int32_t question
= aToken
.FindChar('?');
47 mHashes
.AppendElement(
48 Substring(aToken
, hashStart
, aToken
.Length() - hashStart
));
50 MOZ_ASSERT(question
> 0);
51 if (static_cast<uint32_t>(question
) <= hashStart
) {
53 ("SRIMetadata::SRIMetadata, invalid (options w/o digest)"));
54 return; // invalid metadata
56 mHashes
.AppendElement(Substring(aToken
, hashStart
, question
- hashStart
));
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
;
67 SRIMETADATALOG(("SRIMetadata::SRIMetadata, hash='%s'; alg='%s'",
68 mHashes
[0].get(), mAlgorithm
.get()));
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
);
86 SRIMETADATALOG(("SRIMetadata::operator<, first metadata is empty"));
87 return true; // anything beats the empty metadata (incl. invalid ones)
90 SRIMETADATALOG(("SRIMetadata::operator<, alg1='%d'; alg2='%d'",
91 mAlgorithmType
, aOther
.mAlgorithmType
));
92 return (mAlgorithmType
< aOther
.mAlgorithmType
);
95 bool SRIMetadata::operator>(const SRIMetadata
& aOther
) const {
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
);
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
) {
109 "SRIMetadata::operator+=, appending another '%s' hash (new length=%zu)",
110 mAlgorithm
.get(), mHashes
.Length()));
111 mHashes
.AppendElement(aOther
.mHashes
[0]);
114 MOZ_ASSERT(mHashes
.Length() > 1);
115 MOZ_ASSERT(mHashes
.Length() <= SRIMetadata::MAX_ALTERNATE_HASHES
);
119 bool SRIMetadata::operator==(const SRIMetadata
& aOther
) const {
120 if (IsEmpty() || !IsValid()) {
123 return mAlgorithmType
== aOther
.mAlgorithmType
;
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())) {
132 *outHash
= mHashes
[aIndex
];
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
;
142 case nsICryptoHash::SHA384
:
143 *outLength
= SHA384_LENGTH
;
145 case nsICryptoHash::SHA512
:
146 *outLength
= SHA512_LENGTH
;
151 *outType
= mAlgorithmType
;
154 bool SRIMetadata::CanTrustBeDelegatedTo(const SRIMetadata
& aOther
) const {
156 // No integrity requirements enforced, just let go.
160 if (aOther
.IsEmpty()) {
161 // This metadata requires a check and the other has none, can't delegate.
165 if (mAlgorithmType
!= aOther
.mAlgorithmType
) {
166 // They must use the same hash algorithm.
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()) {
178 for (const auto& hash
: mHashes
) {
179 if (!aOther
.mHashes
.Contains(hash
)) {
187 } // namespace mozilla::dom