Line data Source code
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 "nsDataHashtable.h"
20 : #include "plbase64.h"
21 :
22 : namespace mozilla {
23 : namespace safebrowsing {
24 :
25 : #define PREFIX_SIZE 4
26 : #define COMPLETE_SIZE 32
27 :
28 : // This is the struct that contains 4-byte hash prefixes.
29 : template <uint32_t S, class Comparator>
30 : struct SafebrowsingHash
31 : {
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 :
38 9 : nsresult FromPlaintext(const nsACString& aPlainText, nsICryptoHash* aHash) {
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 :
43 9 : nsresult rv = aHash->Init(nsICryptoHash::SHA256);
44 9 : NS_ENSURE_SUCCESS(rv, rv);
45 :
46 9 : rv = aHash->Update
47 9 : (reinterpret_cast<const uint8_t*>(aPlainText.BeginReading()),
48 : aPlainText.Length());
49 9 : NS_ENSURE_SUCCESS(rv, rv);
50 :
51 18 : nsAutoCString hashed;
52 9 : rv = aHash->Finish(false, hashed);
53 9 : NS_ENSURE_SUCCESS(rv, rv);
54 :
55 9 : NS_ASSERTION(hashed.Length() >= sHashSize,
56 : "not enough characters in the hash");
57 :
58 9 : memcpy(buf, hashed.BeginReading(), sHashSize);
59 :
60 9 : return NS_OK;
61 : }
62 :
63 0 : void Assign(const nsACString& aStr) {
64 0 : NS_ASSERTION(aStr.Length() >= sHashSize,
65 : "string must be at least sHashSize characters long");
66 0 : memcpy(buf, aStr.BeginReading(), sHashSize);
67 0 : }
68 :
69 8 : int Compare(const self_type& aOther) const {
70 8 : return Comparator::Compare(buf, aOther.buf);
71 : }
72 :
73 3 : bool operator==(const self_type& aOther) const {
74 3 : return Comparator::Compare(buf, aOther.buf) == 0;
75 : }
76 :
77 0 : bool operator!=(const self_type& aOther) const {
78 0 : return Comparator::Compare(buf, aOther.buf) != 0;
79 : }
80 :
81 4 : bool operator<(const self_type& aOther) const {
82 4 : return Comparator::Compare(buf, aOther.buf) < 0;
83 : }
84 :
85 0 : void ToString(nsACString& aStr) const {
86 0 : uint32_t len = ((sHashSize + 2) / 3) * 4;
87 :
88 : // Capacity should be one greater than length, because PL_Base64Encode
89 : // will not be null-terminated, while nsCString requires it.
90 0 : aStr.SetCapacity(len + 1);
91 0 : PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting());
92 0 : aStr.BeginWriting()[len] = '\0';
93 0 : aStr.SetLength(len);
94 0 : }
95 :
96 0 : void ToHexString(nsACString& aStr) const {
97 : static const char* const lut = "0123456789ABCDEF";
98 : // 32 bytes is the longest hash
99 0 : size_t len = 32;
100 :
101 0 : aStr.SetCapacity(2 * len);
102 0 : for (size_t i = 0; i < len; ++i) {
103 0 : const char c = static_cast<const char>(buf[i]);
104 0 : aStr.Append(lut[(c >> 4) & 0x0F]);
105 0 : aStr.Append(lut[c & 15]);
106 : }
107 0 : }
108 :
109 4 : uint32_t ToUint32() const {
110 : uint32_t n;
111 4 : memcpy(&n, buf, sizeof(n));
112 4 : return n;
113 : }
114 0 : void FromUint32(uint32_t aHash) {
115 0 : memcpy(buf, &aHash, sizeof(aHash));
116 0 : }
117 : };
118 :
119 : class PrefixComparator {
120 : public:
121 0 : static int Compare(const uint8_t* a, const uint8_t* b) {
122 : uint32_t first;
123 0 : memcpy(&first, a, sizeof(uint32_t));
124 :
125 : uint32_t second;
126 0 : memcpy(&second, b, sizeof(uint32_t));
127 :
128 0 : if (first > second) {
129 0 : return 1;
130 0 : } else if (first == second) {
131 0 : return 0;
132 : } else {
133 0 : return -1;
134 : }
135 : }
136 : };
137 : // Use this for 4-byte hashes
138 : typedef SafebrowsingHash<PREFIX_SIZE, PrefixComparator> Prefix;
139 : typedef nsTArray<Prefix> PrefixArray;
140 :
141 : class CompletionComparator {
142 : public:
143 15 : static int Compare(const uint8_t* a, const uint8_t* b) {
144 15 : return memcmp(a, b, COMPLETE_SIZE);
145 : }
146 : };
147 : // Use this for 32-byte hashes
148 : typedef SafebrowsingHash<COMPLETE_SIZE, CompletionComparator> Completion;
149 : typedef nsTArray<Completion> CompletionArray;
150 :
151 : struct AddPrefix {
152 : // The truncated hash.
153 : Prefix prefix;
154 : // The chunk number to which it belongs.
155 : uint32_t addChunk;
156 :
157 0 : AddPrefix() : addChunk(0) {}
158 :
159 : // Returns the chunk number.
160 0 : uint32_t Chunk() const { return addChunk; }
161 0 : const Prefix &PrefixHash() const { return prefix; }
162 :
163 : template<class T>
164 0 : int Compare(const T& other) const {
165 0 : int cmp = prefix.Compare(other.PrefixHash());
166 0 : if (cmp != 0) {
167 0 : return cmp;
168 : }
169 0 : return addChunk - other.addChunk;
170 : }
171 : };
172 :
173 : struct AddComplete {
174 : Completion complete;
175 : uint32_t addChunk;
176 :
177 28 : AddComplete() : addChunk(0) {}
178 :
179 14 : uint32_t Chunk() const { return addChunk; }
180 : // The 4-byte prefix of the sha256 hash.
181 0 : uint32_t ToUint32() const { return complete.ToUint32(); }
182 : // The 32-byte sha256 hash.
183 15 : const Completion &CompleteHash() const { return complete; }
184 :
185 : template<class T>
186 8 : int Compare(const T& other) const {
187 8 : int cmp = complete.Compare(other.CompleteHash());
188 8 : if (cmp != 0) {
189 6 : return cmp;
190 : }
191 2 : return addChunk - other.addChunk;
192 : }
193 :
194 : bool operator!=(const AddComplete& aOther) const {
195 : if (addChunk != aOther.addChunk) {
196 : return true;
197 : }
198 : return complete != aOther.complete;
199 : }
200 : };
201 :
202 : struct SubPrefix {
203 : // The hash to subtract.
204 : Prefix prefix;
205 : // The chunk number of the add chunk to which the hash belonged.
206 : uint32_t addChunk;
207 : // The chunk number of this sub chunk.
208 : uint32_t subChunk;
209 :
210 0 : SubPrefix(): addChunk(0), subChunk(0) {}
211 :
212 0 : uint32_t Chunk() const { return subChunk; }
213 0 : uint32_t AddChunk() const { return addChunk; }
214 0 : const Prefix &PrefixHash() const { return prefix; }
215 :
216 : template<class T>
217 : // Returns 0 if and only if the chunks are the same in every way.
218 0 : int Compare(const T& aOther) const {
219 0 : int cmp = prefix.Compare(aOther.PrefixHash());
220 0 : if (cmp != 0)
221 0 : return cmp;
222 0 : if (addChunk != aOther.addChunk)
223 0 : return addChunk - aOther.addChunk;
224 0 : return subChunk - aOther.subChunk;
225 : }
226 :
227 : template<class T>
228 0 : int CompareAlt(const T& aOther) const {
229 : Prefix other;
230 0 : other.FromUint32(aOther.ToUint32());
231 0 : int cmp = prefix.Compare(other);
232 0 : if (cmp != 0)
233 0 : return cmp;
234 0 : return addChunk - aOther.addChunk;
235 : }
236 : };
237 :
238 : struct SubComplete {
239 : Completion complete;
240 : uint32_t addChunk;
241 : uint32_t subChunk;
242 :
243 0 : SubComplete() : addChunk(0), subChunk(0) {}
244 :
245 0 : uint32_t Chunk() const { return subChunk; }
246 : uint32_t AddChunk() const { return addChunk; }
247 0 : const Completion &CompleteHash() const { return complete; }
248 : // The 4-byte prefix of the sha256 hash.
249 0 : uint32_t ToUint32() const { return complete.ToUint32(); }
250 :
251 0 : int Compare(const SubComplete& aOther) const {
252 0 : int cmp = complete.Compare(aOther.complete);
253 0 : if (cmp != 0)
254 0 : return cmp;
255 0 : if (addChunk != aOther.addChunk)
256 0 : return addChunk - aOther.addChunk;
257 0 : return subChunk - aOther.subChunk;
258 : }
259 : };
260 :
261 : typedef FallibleTArray<AddPrefix> AddPrefixArray;
262 : typedef FallibleTArray<AddComplete> AddCompleteArray;
263 : typedef FallibleTArray<SubPrefix> SubPrefixArray;
264 : typedef FallibleTArray<SubComplete> SubCompleteArray;
265 : typedef FallibleTArray<Prefix> MissPrefixArray;
266 :
267 : /**
268 : * Compares chunks by their add chunk, then their prefix.
269 : */
270 : template<class T>
271 : class EntryCompare {
272 : public:
273 : typedef T elem_type;
274 2 : static int Compare(const void* e1, const void* e2) {
275 2 : const elem_type* a = static_cast<const elem_type*>(e1);
276 2 : const elem_type* b = static_cast<const elem_type*>(e2);
277 2 : return a->Compare(*b);
278 : }
279 : };
280 :
281 : /**
282 : * Sort an array of store entries. nsTArray::Sort uses Equal/LessThan
283 : * to sort, this does a single Compare so it's a bit quicker over the
284 : * large sorts we do.
285 : */
286 : template<class T, class Alloc>
287 : void
288 48 : EntrySort(nsTArray_Impl<T, Alloc>& aArray)
289 : {
290 48 : qsort(aArray.Elements(), aArray.Length(), sizeof(T),
291 : EntryCompare<T>::Compare);
292 48 : }
293 :
294 : template<class T, class Alloc>
295 : nsresult
296 162 : ReadTArray(nsIInputStream* aStream, nsTArray_Impl<T, Alloc>* aArray, uint32_t aNumElements)
297 : {
298 162 : if (!aArray->SetLength(aNumElements, fallible))
299 0 : return NS_ERROR_OUT_OF_MEMORY;
300 :
301 162 : void *buffer = aArray->Elements();
302 66 : nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer,
303 162 : (aNumElements * sizeof(T)));
304 162 : NS_ENSURE_SUCCESS(rv, rv);
305 162 : return NS_OK;
306 : }
307 :
308 : template<class T, class Alloc>
309 : nsresult
310 120 : WriteTArray(nsIOutputStream* aStream, nsTArray_Impl<T, Alloc>& aArray)
311 : {
312 : uint32_t written;
313 120 : return aStream->Write(reinterpret_cast<char*>(aArray.Elements()),
314 120 : aArray.Length() * sizeof(T),
315 360 : &written);
316 : }
317 :
318 : typedef nsClassHashtable<nsUint32HashKey, nsCString> PrefixStringMap;
319 :
320 : typedef nsDataHashtable<nsCStringHashKey, int64_t> TableFreshnessMap;
321 :
322 : typedef nsCStringHashKey FullHashString;
323 :
324 : typedef nsDataHashtable<FullHashString, int64_t> FullHashExpiryCache;
325 :
326 0 : struct CachedFullHashResponse {
327 : int64_t negativeCacheExpirySec;
328 :
329 : // Map contains all matches found in Fullhash response, this field might be empty.
330 : FullHashExpiryCache fullHashes;
331 :
332 0 : CachedFullHashResponse& operator=(const CachedFullHashResponse& aOther) {
333 0 : negativeCacheExpirySec = aOther.negativeCacheExpirySec;
334 :
335 0 : fullHashes.Clear();
336 0 : for (auto iter = aOther.fullHashes.ConstIter(); !iter.Done(); iter.Next()) {
337 0 : fullHashes.Put(iter.Key(), iter.Data());
338 : }
339 :
340 0 : return *this;
341 : }
342 :
343 0 : bool operator==(const CachedFullHashResponse& aOther) const {
344 0 : if (negativeCacheExpirySec != aOther.negativeCacheExpirySec ||
345 0 : fullHashes.Count() != aOther.fullHashes.Count()) {
346 0 : return false;
347 : }
348 0 : for (auto iter = fullHashes.ConstIter(); !iter.Done(); iter.Next()) {
349 0 : if (iter.Data() != aOther.fullHashes.Get(iter.Key())) {
350 0 : return false;
351 : }
352 : }
353 0 : return true;
354 : }
355 : };
356 :
357 : typedef nsClassHashtable<nsUint32HashKey, CachedFullHashResponse> FullHashResponseMap;
358 :
359 : template<class T>
360 : void
361 6 : CopyClassHashTable(const T& aSource, T& aDestination)
362 : {
363 6 : for (auto iter = aSource.ConstIter(); !iter.Done(); iter.Next()) {
364 0 : auto value = aDestination.LookupOrAdd(iter.Key());
365 0 : *value = *(iter.Data());
366 : }
367 6 : }
368 :
369 : } // namespace safebrowsing
370 : } // namespace mozilla
371 :
372 : #endif // SBEntries_h__
|