| /* |
| * Copyright (C) 2014-2015 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "NetworkCacheKey.h" |
| |
| #include "NetworkCacheCoders.h" |
| #include <wtf/ASCIICType.h> |
| #include <wtf/persistence/PersistentDecoder.h> |
| #include <wtf/persistence/PersistentEncoder.h> |
| #include <wtf/text/CString.h> |
| #include <wtf/text/StringBuilder.h> |
| |
| namespace WebKit { |
| namespace NetworkCache { |
| |
| Key::Key(const Key& o) |
| : m_partition(o.m_partition.isolatedCopy()) |
| , m_type(o.m_type.isolatedCopy()) |
| , m_identifier(o.m_identifier.isolatedCopy()) |
| , m_range(o.m_range.isolatedCopy()) |
| , m_hash(o.m_hash) |
| , m_partitionHash(o.m_partitionHash) |
| { |
| } |
| |
| Key::Key(const String& partition, const String& type, const String& range, const String& identifier, const Salt& salt) |
| : m_partition(partition) |
| , m_type(type) |
| , m_identifier(identifier) |
| , m_range(range) |
| , m_hash(computeHash(salt)) |
| , m_partitionHash(computePartitionHash(salt)) |
| { |
| } |
| |
| Key::Key(WTF::HashTableDeletedValueType) |
| : m_identifier(WTF::HashTableDeletedValue) |
| { |
| } |
| |
| Key::Key(const DataKey& dataKey, const Salt& salt) |
| : m_partition(dataKey.partition) |
| , m_type(dataKey.type) |
| , m_identifier(hashAsString(dataKey.identifier)) |
| , m_hash(computeHash(salt)) |
| , m_partitionHash(computePartitionHash(salt)) |
| { |
| } |
| |
| Key& Key::operator=(const Key& other) |
| { |
| m_partition = other.m_partition.isolatedCopy(); |
| m_type = other.m_type.isolatedCopy(); |
| m_identifier = other.m_identifier.isolatedCopy(); |
| m_range = other.m_range.isolatedCopy(); |
| m_hash = other.m_hash; |
| m_partitionHash = other.m_partitionHash; |
| return *this; |
| } |
| |
| static void hashString(SHA1& sha1, const String& string) |
| { |
| if (string.isNull()) |
| return; |
| |
| if (string.is8Bit() && string.isAllASCII()) { |
| const uint8_t nullByte = 0; |
| sha1.addBytes(string.characters8(), string.length()); |
| sha1.addBytes(&nullByte, 1); |
| return; |
| } |
| auto cString = string.utf8(); |
| // Include terminating null byte. |
| sha1.addBytes(reinterpret_cast<const uint8_t*>(cString.data()), cString.length() + 1); |
| } |
| |
| Key::HashType Key::computeHash(const Salt& salt) const |
| { |
| // We don't really need a cryptographic hash. The key is always verified against the entry header. |
| // SHA1 just happens to be suitably sized, fast and available. |
| SHA1 sha1; |
| sha1.addBytes(salt.data(), salt.size()); |
| |
| hashString(sha1, m_partition); |
| hashString(sha1, m_type); |
| hashString(sha1, m_identifier); |
| hashString(sha1, m_range); |
| |
| SHA1::Digest hash; |
| sha1.computeHash(hash); |
| return hash; |
| } |
| |
| Key::HashType Key::computePartitionHash(const Salt& salt) const |
| { |
| SHA1 sha1; |
| sha1.addBytes(salt.data(), salt.size()); |
| |
| hashString(sha1, m_partition); |
| |
| SHA1::Digest hash; |
| sha1.computeHash(hash); |
| return hash; |
| } |
| |
| String Key::hashAsString(const HashType& hash) |
| { |
| StringBuilder builder; |
| builder.reserveCapacity(hashStringLength()); |
| for (auto byte : hash) { |
| builder.append(upperNibbleToASCIIHexDigit(byte)); |
| builder.append(lowerNibbleToASCIIHexDigit(byte)); |
| } |
| return builder.toString(); |
| } |
| |
| template <typename CharType> bool hexDigitsToHash(CharType* characters, Key::HashType& hash) |
| { |
| for (unsigned i = 0; i < sizeof(hash); ++i) { |
| auto high = characters[2 * i]; |
| auto low = characters[2 * i + 1]; |
| if (!isASCIIHexDigit(high) || !isASCIIHexDigit(low)) |
| return false; |
| hash[i] = toASCIIHexValue(high, low); |
| } |
| return true; |
| } |
| |
| bool Key::stringToHash(const String& string, HashType& hash) |
| { |
| if (string.length() != hashStringLength()) |
| return false; |
| if (string.is8Bit()) |
| return hexDigitsToHash(string.characters8(), hash); |
| return hexDigitsToHash(string.characters16(), hash); |
| } |
| |
| bool Key::operator==(const Key& other) const |
| { |
| return m_hash == other.m_hash && m_partition == other.m_partition && m_type == other.m_type && m_identifier == other.m_identifier && m_range == other.m_range; |
| } |
| |
| void Key::encode(WTF::Persistence::Encoder& encoder) const |
| { |
| encoder << m_partition; |
| encoder << m_type; |
| encoder << m_identifier; |
| encoder << m_range; |
| encoder << m_hash; |
| encoder << m_partitionHash; |
| } |
| |
| bool Key::decode(WTF::Persistence::Decoder& decoder, Key& key) |
| { |
| return decoder.decode(key.m_partition) && decoder.decode(key.m_type) && decoder.decode(key.m_identifier) && decoder.decode(key.m_range) && decoder.decode(key.m_hash) && decoder.decode(key.m_partitionHash); |
| } |
| |
| } |
| } |