Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing 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/. */
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.
10 #ifndef SBEntries_h__
11 #define SBEntries_h__
13 #include "nsTArray.h"
14 #include "nsString.h"
15 #include "nsICryptoHash.h"
16 #include "nsNetUtil.h"
18 #if DEBUG
19 #include "plbase64.h"
20 #endif
22 namespace mozilla {
23 namespace safebrowsing {
25 #define PREFIX_SIZE 4
26 #define COMPLETE_SIZE 32
28 // This is the struct that contains 4-byte hash prefixes.
29 template <uint32_t S, class Comparator>
30 struct SafebrowsingHash
31 {
32 static const uint32_t sHashSize = S;
33 typedef SafebrowsingHash<S, Comparator> self_type;
34 uint8_t buf[S];
36 nsresult FromPlaintext(const nsACString& aPlainText, nsICryptoHash* aHash) {
37 // From the protocol doc:
38 // Each entry in the chunk is composed
39 // of the SHA 256 hash of a suffix/prefix expression.
41 nsresult rv = aHash->Init(nsICryptoHash::SHA256);
42 NS_ENSURE_SUCCESS(rv, rv);
44 rv = aHash->Update
45 (reinterpret_cast<const uint8_t*>(aPlainText.BeginReading()),
46 aPlainText.Length());
47 NS_ENSURE_SUCCESS(rv, rv);
49 nsAutoCString hashed;
50 rv = aHash->Finish(false, hashed);
51 NS_ENSURE_SUCCESS(rv, rv);
53 NS_ASSERTION(hashed.Length() >= sHashSize,
54 "not enough characters in the hash");
56 memcpy(buf, hashed.BeginReading(), sHashSize);
58 return NS_OK;
59 }
61 void Assign(const nsACString& aStr) {
62 NS_ASSERTION(aStr.Length() >= sHashSize,
63 "string must be at least sHashSize characters long");
64 memcpy(buf, aStr.BeginReading(), sHashSize);
65 }
67 int Compare(const self_type& aOther) const {
68 return Comparator::Compare(buf, aOther.buf);
69 }
71 bool operator==(const self_type& aOther) const {
72 return Comparator::Compare(buf, aOther.buf) == 0;
73 }
75 bool operator!=(const self_type& aOther) const {
76 return Comparator::Compare(buf, aOther.buf) != 0;
77 }
79 bool operator<(const self_type& aOther) const {
80 return Comparator::Compare(buf, aOther.buf) < 0;
81 }
83 #ifdef DEBUG
84 void ToString(nsACString& aStr) const {
85 uint32_t len = ((sHashSize + 2) / 3) * 4;
86 aStr.SetCapacity(len + 1);
87 PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting());
88 aStr.BeginWriting()[len] = '\0';
89 }
91 void ToHexString(nsACString& aStr) const {
92 static const char* const lut = "0123456789ABCDEF";
93 // 32 bytes is the longest hash
94 size_t len = 32;
96 aStr.SetCapacity(2 * len);
97 for (size_t i = 0; i < len; ++i) {
98 const char c = static_cast<const char>(buf[i]);
99 aStr.Append(lut[(c >> 4) & 0x0F]);
100 aStr.Append(lut[c & 15]);
101 }
102 }
103 #endif
104 uint32_t ToUint32() const {
105 return *((uint32_t*)buf);
106 }
107 void FromUint32(uint32_t aHash) {
108 *((uint32_t*)buf) = aHash;
109 }
110 };
112 class PrefixComparator {
113 public:
114 static int Compare(const uint8_t* a, const uint8_t* b) {
115 uint32_t first = *((uint32_t*)a);
116 uint32_t second = *((uint32_t*)b);
117 if (first > second) {
118 return 1;
119 } else if (first == second) {
120 return 0;
121 } else {
122 return -1;
123 }
124 }
125 };
126 // Use this for 4-byte hashes
127 typedef SafebrowsingHash<PREFIX_SIZE, PrefixComparator> Prefix;
128 typedef nsTArray<Prefix> PrefixArray;
130 class CompletionComparator {
131 public:
132 static int Compare(const uint8_t* a, const uint8_t* b) {
133 return memcmp(a, b, COMPLETE_SIZE);
134 }
135 };
136 // Use this for 32-byte hashes
137 typedef SafebrowsingHash<COMPLETE_SIZE, CompletionComparator> Completion;
138 typedef nsTArray<Completion> CompletionArray;
140 struct AddPrefix {
141 // The truncated hash.
142 Prefix prefix;
143 // The chunk number to which it belongs.
144 uint32_t addChunk;
146 AddPrefix() : addChunk(0) {}
148 // Returns the chunk number.
149 uint32_t Chunk() const { return addChunk; }
150 const Prefix &PrefixHash() const { return prefix; }
152 template<class T>
153 int Compare(const T& other) const {
154 int cmp = prefix.Compare(other.PrefixHash());
155 if (cmp != 0) {
156 return cmp;
157 }
158 return addChunk - other.addChunk;
159 }
160 };
162 struct AddComplete {
163 Completion complete;
164 uint32_t addChunk;
166 AddComplete() : addChunk(0) {}
168 uint32_t Chunk() const { return addChunk; }
169 // The 4-byte prefix of the sha256 hash.
170 uint32_t ToUint32() const { return complete.ToUint32(); }
171 // The 32-byte sha256 hash.
172 const Completion &CompleteHash() const { return complete; }
174 template<class T>
175 int Compare(const T& other) const {
176 int cmp = complete.Compare(other.CompleteHash());
177 if (cmp != 0) {
178 return cmp;
179 }
180 return addChunk - other.addChunk;
181 }
182 };
184 struct SubPrefix {
185 // The hash to subtract.
186 Prefix prefix;
187 // The chunk number of the add chunk to which the hash belonged.
188 uint32_t addChunk;
189 // The chunk number of this sub chunk.
190 uint32_t subChunk;
192 SubPrefix(): addChunk(0), subChunk(0) {}
194 uint32_t Chunk() const { return subChunk; }
195 uint32_t AddChunk() const { return addChunk; }
196 const Prefix &PrefixHash() const { return prefix; }
198 template<class T>
199 // Returns 0 if and only if the chunks are the same in every way.
200 int Compare(const T& aOther) const {
201 int cmp = prefix.Compare(aOther.PrefixHash());
202 if (cmp != 0)
203 return cmp;
204 if (addChunk != aOther.addChunk)
205 return addChunk - aOther.addChunk;
206 return subChunk - aOther.subChunk;
207 }
209 template<class T>
210 int CompareAlt(const T& aOther) const {
211 Prefix other;
212 other.FromUint32(aOther.ToUint32());
213 int cmp = prefix.Compare(other);
214 if (cmp != 0)
215 return cmp;
216 return addChunk - aOther.addChunk;
217 }
218 };
220 struct SubComplete {
221 Completion complete;
222 uint32_t addChunk;
223 uint32_t subChunk;
225 SubComplete() : addChunk(0), subChunk(0) {}
227 uint32_t Chunk() const { return subChunk; }
228 uint32_t AddChunk() const { return addChunk; }
229 const Completion &CompleteHash() const { return complete; }
230 // The 4-byte prefix of the sha256 hash.
231 uint32_t ToUint32() const { return complete.ToUint32(); }
233 int Compare(const SubComplete& aOther) const {
234 int cmp = complete.Compare(aOther.complete);
235 if (cmp != 0)
236 return cmp;
237 if (addChunk != aOther.addChunk)
238 return addChunk - aOther.addChunk;
239 return subChunk - aOther.subChunk;
240 }
241 };
243 typedef FallibleTArray<AddPrefix> AddPrefixArray;
244 typedef FallibleTArray<AddComplete> AddCompleteArray;
245 typedef FallibleTArray<SubPrefix> SubPrefixArray;
246 typedef FallibleTArray<SubComplete> SubCompleteArray;
248 /**
249 * Compares chunks by their add chunk, then their prefix.
250 */
251 template<class T>
252 class EntryCompare {
253 public:
254 typedef T elem_type;
255 static int Compare(const void* e1, const void* e2) {
256 const elem_type* a = static_cast<const elem_type*>(e1);
257 const elem_type* b = static_cast<const elem_type*>(e2);
258 return a->Compare(*b);
259 }
260 };
262 /**
263 * Sort an array of store entries. nsTArray::Sort uses Equal/LessThan
264 * to sort, this does a single Compare so it's a bit quicker over the
265 * large sorts we do.
266 */
267 template<class T, class Alloc>
268 void
269 EntrySort(nsTArray_Impl<T, Alloc>& aArray)
270 {
271 qsort(aArray.Elements(), aArray.Length(), sizeof(T),
272 EntryCompare<T>::Compare);
273 }
275 template<class T, class Alloc>
276 nsresult
277 ReadTArray(nsIInputStream* aStream, nsTArray_Impl<T, Alloc>* aArray, uint32_t aNumElements)
278 {
279 aArray->SetLength(aNumElements);
281 void *buffer = aArray->Elements();
282 nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer,
283 (aNumElements * sizeof(T)));
284 NS_ENSURE_SUCCESS(rv, rv);
285 return NS_OK;
286 }
288 template<class T>
289 nsresult
290 ReadTArray(nsIInputStream* aStream, FallibleTArray<T>* aArray, uint32_t aNumElements)
291 {
292 if (!aArray->SetLength(aNumElements))
293 return NS_ERROR_OUT_OF_MEMORY;
295 void *buffer = aArray->Elements();
296 nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer,
297 (aNumElements * sizeof(T)));
298 NS_ENSURE_SUCCESS(rv, rv);
299 return NS_OK;
300 }
302 template<class T, class Alloc>
303 nsresult
304 WriteTArray(nsIOutputStream* aStream, nsTArray_Impl<T, Alloc>& aArray)
305 {
306 uint32_t written;
307 return aStream->Write(reinterpret_cast<char*>(aArray.Elements()),
308 aArray.Length() * sizeof(T),
309 &written);
310 }
312 } // namespace safebrowsing
313 } // namespace mozilla
314 #endif // SBEntries_h__