michael@0: //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // This header file defines the storage types of the actual safebrowsing michael@0: // chunk data, which may be either 32-bit hashes or complete 256-bit hashes. michael@0: // Chunk numbers are represented in ChunkSet.h. michael@0: michael@0: #ifndef SBEntries_h__ michael@0: #define SBEntries_h__ michael@0: michael@0: #include "nsTArray.h" michael@0: #include "nsString.h" michael@0: #include "nsICryptoHash.h" michael@0: #include "nsNetUtil.h" michael@0: michael@0: #if DEBUG michael@0: #include "plbase64.h" michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace safebrowsing { michael@0: michael@0: #define PREFIX_SIZE 4 michael@0: #define COMPLETE_SIZE 32 michael@0: michael@0: // This is the struct that contains 4-byte hash prefixes. michael@0: template michael@0: struct SafebrowsingHash michael@0: { michael@0: static const uint32_t sHashSize = S; michael@0: typedef SafebrowsingHash self_type; michael@0: uint8_t buf[S]; michael@0: michael@0: nsresult FromPlaintext(const nsACString& aPlainText, nsICryptoHash* aHash) { michael@0: // From the protocol doc: michael@0: // Each entry in the chunk is composed michael@0: // of the SHA 256 hash of a suffix/prefix expression. michael@0: michael@0: nsresult rv = aHash->Init(nsICryptoHash::SHA256); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = aHash->Update michael@0: (reinterpret_cast(aPlainText.BeginReading()), michael@0: aPlainText.Length()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString hashed; michael@0: rv = aHash->Finish(false, hashed); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ASSERTION(hashed.Length() >= sHashSize, michael@0: "not enough characters in the hash"); michael@0: michael@0: memcpy(buf, hashed.BeginReading(), sHashSize); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void Assign(const nsACString& aStr) { michael@0: NS_ASSERTION(aStr.Length() >= sHashSize, michael@0: "string must be at least sHashSize characters long"); michael@0: memcpy(buf, aStr.BeginReading(), sHashSize); michael@0: } michael@0: michael@0: int Compare(const self_type& aOther) const { michael@0: return Comparator::Compare(buf, aOther.buf); michael@0: } michael@0: michael@0: bool operator==(const self_type& aOther) const { michael@0: return Comparator::Compare(buf, aOther.buf) == 0; michael@0: } michael@0: michael@0: bool operator!=(const self_type& aOther) const { michael@0: return Comparator::Compare(buf, aOther.buf) != 0; michael@0: } michael@0: michael@0: bool operator<(const self_type& aOther) const { michael@0: return Comparator::Compare(buf, aOther.buf) < 0; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void ToString(nsACString& aStr) const { michael@0: uint32_t len = ((sHashSize + 2) / 3) * 4; michael@0: aStr.SetCapacity(len + 1); michael@0: PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting()); michael@0: aStr.BeginWriting()[len] = '\0'; michael@0: } michael@0: michael@0: void ToHexString(nsACString& aStr) const { michael@0: static const char* const lut = "0123456789ABCDEF"; michael@0: // 32 bytes is the longest hash michael@0: size_t len = 32; michael@0: michael@0: aStr.SetCapacity(2 * len); michael@0: for (size_t i = 0; i < len; ++i) { michael@0: const char c = static_cast(buf[i]); michael@0: aStr.Append(lut[(c >> 4) & 0x0F]); michael@0: aStr.Append(lut[c & 15]); michael@0: } michael@0: } michael@0: #endif michael@0: uint32_t ToUint32() const { michael@0: return *((uint32_t*)buf); michael@0: } michael@0: void FromUint32(uint32_t aHash) { michael@0: *((uint32_t*)buf) = aHash; michael@0: } michael@0: }; michael@0: michael@0: class PrefixComparator { michael@0: public: michael@0: static int Compare(const uint8_t* a, const uint8_t* b) { michael@0: uint32_t first = *((uint32_t*)a); michael@0: uint32_t second = *((uint32_t*)b); michael@0: if (first > second) { michael@0: return 1; michael@0: } else if (first == second) { michael@0: return 0; michael@0: } else { michael@0: return -1; michael@0: } michael@0: } michael@0: }; michael@0: // Use this for 4-byte hashes michael@0: typedef SafebrowsingHash Prefix; michael@0: typedef nsTArray PrefixArray; michael@0: michael@0: class CompletionComparator { michael@0: public: michael@0: static int Compare(const uint8_t* a, const uint8_t* b) { michael@0: return memcmp(a, b, COMPLETE_SIZE); michael@0: } michael@0: }; michael@0: // Use this for 32-byte hashes michael@0: typedef SafebrowsingHash Completion; michael@0: typedef nsTArray CompletionArray; michael@0: michael@0: struct AddPrefix { michael@0: // The truncated hash. michael@0: Prefix prefix; michael@0: // The chunk number to which it belongs. michael@0: uint32_t addChunk; michael@0: michael@0: AddPrefix() : addChunk(0) {} michael@0: michael@0: // Returns the chunk number. michael@0: uint32_t Chunk() const { return addChunk; } michael@0: const Prefix &PrefixHash() const { return prefix; } michael@0: michael@0: template michael@0: int Compare(const T& other) const { michael@0: int cmp = prefix.Compare(other.PrefixHash()); michael@0: if (cmp != 0) { michael@0: return cmp; michael@0: } michael@0: return addChunk - other.addChunk; michael@0: } michael@0: }; michael@0: michael@0: struct AddComplete { michael@0: Completion complete; michael@0: uint32_t addChunk; michael@0: michael@0: AddComplete() : addChunk(0) {} michael@0: michael@0: uint32_t Chunk() const { return addChunk; } michael@0: // The 4-byte prefix of the sha256 hash. michael@0: uint32_t ToUint32() const { return complete.ToUint32(); } michael@0: // The 32-byte sha256 hash. michael@0: const Completion &CompleteHash() const { return complete; } michael@0: michael@0: template michael@0: int Compare(const T& other) const { michael@0: int cmp = complete.Compare(other.CompleteHash()); michael@0: if (cmp != 0) { michael@0: return cmp; michael@0: } michael@0: return addChunk - other.addChunk; michael@0: } michael@0: }; michael@0: michael@0: struct SubPrefix { michael@0: // The hash to subtract. michael@0: Prefix prefix; michael@0: // The chunk number of the add chunk to which the hash belonged. michael@0: uint32_t addChunk; michael@0: // The chunk number of this sub chunk. michael@0: uint32_t subChunk; michael@0: michael@0: SubPrefix(): addChunk(0), subChunk(0) {} michael@0: michael@0: uint32_t Chunk() const { return subChunk; } michael@0: uint32_t AddChunk() const { return addChunk; } michael@0: const Prefix &PrefixHash() const { return prefix; } michael@0: michael@0: template michael@0: // Returns 0 if and only if the chunks are the same in every way. michael@0: int Compare(const T& aOther) const { michael@0: int cmp = prefix.Compare(aOther.PrefixHash()); michael@0: if (cmp != 0) michael@0: return cmp; michael@0: if (addChunk != aOther.addChunk) michael@0: return addChunk - aOther.addChunk; michael@0: return subChunk - aOther.subChunk; michael@0: } michael@0: michael@0: template michael@0: int CompareAlt(const T& aOther) const { michael@0: Prefix other; michael@0: other.FromUint32(aOther.ToUint32()); michael@0: int cmp = prefix.Compare(other); michael@0: if (cmp != 0) michael@0: return cmp; michael@0: return addChunk - aOther.addChunk; michael@0: } michael@0: }; michael@0: michael@0: struct SubComplete { michael@0: Completion complete; michael@0: uint32_t addChunk; michael@0: uint32_t subChunk; michael@0: michael@0: SubComplete() : addChunk(0), subChunk(0) {} michael@0: michael@0: uint32_t Chunk() const { return subChunk; } michael@0: uint32_t AddChunk() const { return addChunk; } michael@0: const Completion &CompleteHash() const { return complete; } michael@0: // The 4-byte prefix of the sha256 hash. michael@0: uint32_t ToUint32() const { return complete.ToUint32(); } michael@0: michael@0: int Compare(const SubComplete& aOther) const { michael@0: int cmp = complete.Compare(aOther.complete); michael@0: if (cmp != 0) michael@0: return cmp; michael@0: if (addChunk != aOther.addChunk) michael@0: return addChunk - aOther.addChunk; michael@0: return subChunk - aOther.subChunk; michael@0: } michael@0: }; michael@0: michael@0: typedef FallibleTArray AddPrefixArray; michael@0: typedef FallibleTArray AddCompleteArray; michael@0: typedef FallibleTArray SubPrefixArray; michael@0: typedef FallibleTArray SubCompleteArray; michael@0: michael@0: /** michael@0: * Compares chunks by their add chunk, then their prefix. michael@0: */ michael@0: template michael@0: class EntryCompare { michael@0: public: michael@0: typedef T elem_type; michael@0: static int Compare(const void* e1, const void* e2) { michael@0: const elem_type* a = static_cast(e1); michael@0: const elem_type* b = static_cast(e2); michael@0: return a->Compare(*b); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Sort an array of store entries. nsTArray::Sort uses Equal/LessThan michael@0: * to sort, this does a single Compare so it's a bit quicker over the michael@0: * large sorts we do. michael@0: */ michael@0: template michael@0: void michael@0: EntrySort(nsTArray_Impl& aArray) michael@0: { michael@0: qsort(aArray.Elements(), aArray.Length(), sizeof(T), michael@0: EntryCompare::Compare); michael@0: } michael@0: michael@0: template michael@0: nsresult michael@0: ReadTArray(nsIInputStream* aStream, nsTArray_Impl* aArray, uint32_t aNumElements) michael@0: { michael@0: aArray->SetLength(aNumElements); michael@0: michael@0: void *buffer = aArray->Elements(); michael@0: nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer, michael@0: (aNumElements * sizeof(T))); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: template michael@0: nsresult michael@0: ReadTArray(nsIInputStream* aStream, FallibleTArray* aArray, uint32_t aNumElements) michael@0: { michael@0: if (!aArray->SetLength(aNumElements)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: void *buffer = aArray->Elements(); michael@0: nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer, michael@0: (aNumElements * sizeof(T))); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: template michael@0: nsresult michael@0: WriteTArray(nsIOutputStream* aStream, nsTArray_Impl& aArray) michael@0: { michael@0: uint32_t written; michael@0: return aStream->Write(reinterpret_cast(aArray.Elements()), michael@0: aArray.Length() * sizeof(T), michael@0: &written); michael@0: } michael@0: michael@0: } // namespace safebrowsing michael@0: } // namespace mozilla michael@0: #endif // SBEntries_h__