1 //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 // This header file defines the storage types of the actual safebrowsing
7 // chunk data, which may be either 32-bit hashes or complete 256-bit hashes.
8 // Chunk numbers are represented in ChunkSet.h.
9
10 #ifndef SBEntries_h__
11 #define SBEntries_h__
12
13 #include "nsTArray.h"
14 #include "nsString.h"
15 #include "nsICryptoHash.h"
16 #include "nsNetUtil.h"
17 #include "nsIOutputStream.h"
18 #include "nsClassHashtable.h"
19 #include "nsComponentManagerUtils.h"
20 #include "nsTHashMap.h"
21 #include "plbase64.h"
22
23 namespace mozilla {
24 namespace safebrowsing {
25
26 #define PREFIX_SIZE 4
27 #define COMPLETE_SIZE 32
28
29 // This is the struct that contains 4-byte hash prefixes.
30 template <uint32_t S, class Comparator>
31 struct SafebrowsingHash {
32 static_assert(S >= 4, "The SafebrowsingHash should be at least 4 bytes.");
33
34 static const uint32_t sHashSize = S;
35 typedef SafebrowsingHash<S, Comparator> self_type;
36 uint8_t buf[S];
37
FromPlaintextSafebrowsingHash38 nsresult FromPlaintext(const nsACString& aPlainText) {
39 // From the protocol doc:
40 // Each entry in the chunk is composed
41 // of the SHA 256 hash of a suffix/prefix expression.
42 nsresult rv;
43 nsCOMPtr<nsICryptoHash> hash =
44 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
45 NS_ENSURE_SUCCESS(rv, rv);
46
47 rv = hash->Init(nsICryptoHash::SHA256);
48 NS_ENSURE_SUCCESS(rv, rv);
49
50 rv = hash->Update(
51 reinterpret_cast<const uint8_t*>(aPlainText.BeginReading()),
52 aPlainText.Length());
53 NS_ENSURE_SUCCESS(rv, rv);
54
55 nsAutoCString hashed;
56 rv = hash->Finish(false, hashed);
57 NS_ENSURE_SUCCESS(rv, rv);
58
59 NS_ASSERTION(hashed.Length() >= sHashSize,
60 "not enough characters in the hash");
61
62 memcpy(buf, hashed.BeginReading(), sHashSize);
63
64 return NS_OK;
65 }
66
AssignSafebrowsingHash67 void Assign(const nsACString& aStr) {
68 NS_ASSERTION(aStr.Length() >= sHashSize,
69 "string must be at least sHashSize characters long");
70 memcpy(buf, aStr.BeginReading(), sHashSize);
71 }
72
CompareSafebrowsingHash73 int Compare(const self_type& aOther) const {
74 return Comparator::Compare(buf, aOther.buf);
75 }
76
77 bool operator==(const self_type& aOther) const {
78 return Comparator::Compare(buf, aOther.buf) == 0;
79 }
80
81 bool operator!=(const self_type& aOther) const {
82 return Comparator::Compare(buf, aOther.buf) != 0;
83 }
84
85 bool operator<(const self_type& aOther) const {
86 return Comparator::Compare(buf, aOther.buf) < 0;
87 }
88
ToStringSafebrowsingHash89 void ToString(nsACString& aStr) const {
90 // Base64 represents 6-bits data as 8-bits output.
91 uint32_t len = ((sHashSize + 2) / 3) * 4;
92
93 aStr.SetLength(len);
94 PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting());
95 MOZ_ASSERT(aStr.BeginReading()[len] == '\0');
96 }
97
ToStringSafebrowsingHash98 nsCString ToString() const {
99 nsAutoCString str;
100 ToString(str);
101 return std::move(str);
102 }
103
ToHexStringSafebrowsingHash104 void ToHexString(nsACString& aStr) const {
105 static const char* const lut = "0123456789ABCDEF";
106 // 32 bytes is the longest hash
107 size_t len = 32;
108
109 aStr.SetCapacity(2 * len);
110 for (size_t i = 0; i < len; ++i) {
111 const char c = static_cast<char>(buf[i]);
112 aStr.Append(lut[(c >> 4) & 0x0F]);
113 aStr.Append(lut[c & 15]);
114 }
115 }
116
ToUint32SafebrowsingHash117 uint32_t ToUint32() const {
118 uint32_t n;
119 memcpy(&n, buf, sizeof(n));
120 return n;
121 }
FromUint32SafebrowsingHash122 void FromUint32(uint32_t aHash) { memcpy(buf, &aHash, sizeof(aHash)); }
123 };
124
125 class PrefixComparator {
126 public:
Compare(const uint8_t * a,const uint8_t * b)127 static int Compare(const uint8_t* a, const uint8_t* b) {
128 uint32_t first;
129 memcpy(&first, a, sizeof(uint32_t));
130
131 uint32_t second;
132 memcpy(&second, b, sizeof(uint32_t));
133
134 if (first > second) {
135 return 1;
136 } else if (first == second) {
137 return 0;
138 } else {
139 return -1;
140 }
141 }
142 };
143 // Use this for 4-byte hashes
144 typedef SafebrowsingHash<PREFIX_SIZE, PrefixComparator> Prefix;
145 typedef nsTArray<Prefix> PrefixArray;
146
147 class CompletionComparator {
148 public:
Compare(const uint8_t * a,const uint8_t * b)149 static int Compare(const uint8_t* a, const uint8_t* b) {
150 return memcmp(a, b, COMPLETE_SIZE);
151 }
152 };
153 // Use this for 32-byte hashes
154 typedef SafebrowsingHash<COMPLETE_SIZE, CompletionComparator> Completion;
155 typedef nsTArray<Completion> CompletionArray;
156
157 struct AddPrefix {
158 // The truncated hash.
159 Prefix prefix;
160 // The chunk number to which it belongs.
161 uint32_t addChunk;
162
AddPrefixAddPrefix163 AddPrefix() : addChunk(0) {}
164
165 // Returns the chunk number.
ChunkAddPrefix166 uint32_t Chunk() const { return addChunk; }
PrefixHashAddPrefix167 const Prefix& PrefixHash() const { return prefix; }
168
169 template <class T>
CompareAddPrefix170 int Compare(const T& other) const {
171 int cmp = prefix.Compare(other.PrefixHash());
172 if (cmp != 0) {
173 return cmp;
174 }
175 return addChunk - other.addChunk;
176 }
177 };
178
179 struct AddComplete {
180 Completion complete;
181 uint32_t addChunk;
182
AddCompleteAddComplete183 AddComplete() : addChunk(0) {}
184
ChunkAddComplete185 uint32_t Chunk() const { return addChunk; }
186 // The 4-byte prefix of the sha256 hash.
ToUint32AddComplete187 uint32_t ToUint32() const { return complete.ToUint32(); }
188 // The 32-byte sha256 hash.
CompleteHashAddComplete189 const Completion& CompleteHash() const { return complete; }
190
191 template <class T>
CompareAddComplete192 int Compare(const T& other) const {
193 int cmp = complete.Compare(other.CompleteHash());
194 if (cmp != 0) {
195 return cmp;
196 }
197 return addChunk - other.addChunk;
198 }
199
200 bool operator!=(const AddComplete& aOther) const {
201 if (addChunk != aOther.addChunk) {
202 return true;
203 }
204 return complete != aOther.complete;
205 }
206 };
207
208 struct SubPrefix {
209 // The hash to subtract.
210 Prefix prefix;
211 // The chunk number of the add chunk to which the hash belonged.
212 uint32_t addChunk;
213 // The chunk number of this sub chunk.
214 uint32_t subChunk;
215
SubPrefixSubPrefix216 SubPrefix() : addChunk(0), subChunk(0) {}
217
ChunkSubPrefix218 uint32_t Chunk() const { return subChunk; }
AddChunkSubPrefix219 uint32_t AddChunk() const { return addChunk; }
PrefixHashSubPrefix220 const Prefix& PrefixHash() const { return prefix; }
221
222 template <class T>
223 // Returns 0 if and only if the chunks are the same in every way.
CompareSubPrefix224 int Compare(const T& aOther) const {
225 int cmp = prefix.Compare(aOther.PrefixHash());
226 if (cmp != 0) return cmp;
227 if (addChunk != aOther.addChunk) return addChunk - aOther.addChunk;
228 return subChunk - aOther.subChunk;
229 }
230
231 template <class T>
CompareAltSubPrefix232 int CompareAlt(const T& aOther) const {
233 Prefix other;
234 other.FromUint32(aOther.ToUint32());
235 int cmp = prefix.Compare(other);
236 if (cmp != 0) return cmp;
237 return addChunk - aOther.addChunk;
238 }
239 };
240
241 struct SubComplete {
242 Completion complete;
243 uint32_t addChunk;
244 uint32_t subChunk;
245
SubCompleteSubComplete246 SubComplete() : addChunk(0), subChunk(0) {}
247
ChunkSubComplete248 uint32_t Chunk() const { return subChunk; }
AddChunkSubComplete249 uint32_t AddChunk() const { return addChunk; }
CompleteHashSubComplete250 const Completion& CompleteHash() const { return complete; }
251 // The 4-byte prefix of the sha256 hash.
ToUint32SubComplete252 uint32_t ToUint32() const { return complete.ToUint32(); }
253
CompareSubComplete254 int Compare(const SubComplete& aOther) const {
255 int cmp = complete.Compare(aOther.complete);
256 if (cmp != 0) return cmp;
257 if (addChunk != aOther.addChunk) return addChunk - aOther.addChunk;
258 return subChunk - aOther.subChunk;
259 }
260 };
261
262 typedef FallibleTArray<AddPrefix> AddPrefixArray;
263 typedef FallibleTArray<AddComplete> AddCompleteArray;
264 typedef FallibleTArray<SubPrefix> SubPrefixArray;
265 typedef FallibleTArray<SubComplete> SubCompleteArray;
266 typedef FallibleTArray<Prefix> MissPrefixArray;
267
268 /**
269 * Compares chunks by their add chunk, then their prefix.
270 */
271 template <class T>
272 class EntryCompare {
273 public:
274 typedef T elem_type;
Compare(const void * e1,const void * e2)275 static int Compare(const void* e1, const void* e2) {
276 const elem_type* a = static_cast<const elem_type*>(e1);
277 const elem_type* b = static_cast<const elem_type*>(e2);
278 return a->Compare(*b);
279 }
280 };
281
282 /**
283 * Sort an array of store entries. nsTArray::Sort uses Equal/LessThan
284 * to sort, this does a single Compare so it's a bit quicker over the
285 * large sorts we do.
286 */
287 template <class T, class Alloc>
EntrySort(nsTArray_Impl<T,Alloc> & aArray)288 void EntrySort(nsTArray_Impl<T, Alloc>& aArray) {
289 qsort(aArray.Elements(), aArray.Length(), sizeof(T),
290 EntryCompare<T>::Compare);
291 }
292
293 template <class T, class Alloc>
ReadTArray(nsIInputStream * aStream,nsTArray_Impl<T,Alloc> * aArray,uint32_t aNumElements)294 nsresult ReadTArray(nsIInputStream* aStream, nsTArray_Impl<T, Alloc>* aArray,
295 uint32_t aNumElements) {
296 if (!aArray->SetLength(aNumElements, fallible)) return NS_ERROR_OUT_OF_MEMORY;
297
298 void* buffer = aArray->Elements();
299 nsresult rv =
300 NS_ReadInputStreamToBuffer(aStream, &buffer, (aNumElements * sizeof(T)));
301 NS_ENSURE_SUCCESS(rv, rv);
302 return NS_OK;
303 }
304
305 template <class T, class Alloc>
WriteTArray(nsIOutputStream * aStream,nsTArray_Impl<T,Alloc> & aArray)306 nsresult WriteTArray(nsIOutputStream* aStream,
307 nsTArray_Impl<T, Alloc>& aArray) {
308 uint32_t written;
309 return aStream->Write(reinterpret_cast<char*>(aArray.Elements()),
310 aArray.Length() * sizeof(T), &written);
311 }
312
313 typedef nsClassHashtable<nsUint32HashKey, nsCString> PrefixStringMap;
314
315 typedef nsTHashMap<nsCStringHashKey, int64_t> TableFreshnessMap;
316
317 typedef nsCStringHashKey FullHashString;
318
319 typedef nsTHashMap<FullHashString, int64_t> FullHashExpiryCache;
320
321 struct CachedFullHashResponse {
322 int64_t negativeCacheExpirySec;
323
324 // Map contains all matches found in Fullhash response, this field might be
325 // empty.
326 FullHashExpiryCache fullHashes;
327
328 CachedFullHashResponse& operator=(const CachedFullHashResponse& aOther) {
329 negativeCacheExpirySec = aOther.negativeCacheExpirySec;
330
331 fullHashes = aOther.fullHashes.Clone();
332
333 return *this;
334 }
335
336 bool operator==(const CachedFullHashResponse& aOther) const {
337 if (negativeCacheExpirySec != aOther.negativeCacheExpirySec ||
338 fullHashes.Count() != aOther.fullHashes.Count()) {
339 return false;
340 }
341 for (const auto& entry : fullHashes) {
342 if (entry.GetData() != aOther.fullHashes.Get(entry.GetKey())) {
343 return false;
344 }
345 }
346 return true;
347 }
348 };
349
350 typedef nsClassHashtable<nsUint32HashKey, CachedFullHashResponse>
351 FullHashResponseMap;
352
353 template <class T>
CopyClassHashTable(const T & aSource,T & aDestination)354 void CopyClassHashTable(const T& aSource, T& aDestination) {
355 for (const auto& entry : aSource) {
356 auto value = aDestination.GetOrInsertNew(entry.GetKey());
357 *value = *(entry.GetData());
358 }
359 }
360
361 } // namespace safebrowsing
362 } // namespace mozilla
363
364 #endif // SBEntries_h__
365