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