1/*
2 * Copyright (C) 2014, 2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#include "config.h"
26#include "IDBSerialization.h"
27
28#if ENABLE(INDEXED_DATABASE)
29
30#include "IDBKeyData.h"
31#include "IDBKeyPath.h"
32#include "KeyedCoding.h"
33
34#if USE(GLIB)
35#include <glib.h>
36#include <wtf/glib/GRefPtr.h>
37#endif
38
39namespace WebCore {
40
41enum class KeyPathType { Null, String, Array };
42
43RefPtr<SharedBuffer> serializeIDBKeyPath(const Optional<IDBKeyPath>& keyPath)
44{
45 auto encoder = KeyedEncoder::encoder();
46
47 if (keyPath) {
48 auto visitor = WTF::makeVisitor([&](const String& string) {
49 encoder->encodeEnum("type", KeyPathType::String);
50 encoder->encodeString("string", string);
51 }, [&](const Vector<String>& vector) {
52 encoder->encodeEnum("type", KeyPathType::Array);
53 encoder->encodeObjects("array", vector.begin(), vector.end(), [](WebCore::KeyedEncoder& encoder, const String& string) {
54 encoder.encodeString("string", string);
55 });
56 });
57 WTF::visit(visitor, keyPath.value());
58 } else
59 encoder->encodeEnum("type", KeyPathType::Null);
60
61 return encoder->finishEncoding();
62}
63
64bool deserializeIDBKeyPath(const uint8_t* data, size_t size, Optional<IDBKeyPath>& result)
65{
66 if (!data || !size)
67 return false;
68
69 auto decoder = KeyedDecoder::decoder(data, size);
70
71 KeyPathType type;
72 bool succeeded = decoder->decodeEnum("type", type, [](KeyPathType value) {
73 return value == KeyPathType::Null || value == KeyPathType::String || value == KeyPathType::Array;
74 });
75 if (!succeeded)
76 return false;
77
78 switch (type) {
79 case KeyPathType::Null:
80 break;
81 case KeyPathType::String: {
82 String string;
83 if (!decoder->decodeString("string", string))
84 return false;
85 result = IDBKeyPath(WTFMove(string));
86 break;
87 }
88 case KeyPathType::Array: {
89 Vector<String> vector;
90 succeeded = decoder->decodeObjects("array", vector, [](KeyedDecoder& decoder, String& result) {
91 return decoder.decodeString("string", result);
92 });
93 if (!succeeded)
94 return false;
95 result = IDBKeyPath(WTFMove(vector));
96 break;
97 }
98 }
99 return true;
100}
101
102static bool isLegacySerializedIDBKeyData(const uint8_t* data, size_t size)
103{
104#if USE(CF)
105 UNUSED_PARAM(size);
106
107 // This is the magic character that begins serialized PropertyLists, and tells us whether
108 // the key we're looking at is an old-style key.
109 static const uint8_t legacySerializedKeyVersion = 'b';
110 if (data[0] == legacySerializedKeyVersion)
111 return true;
112#elif USE(GLIB)
113 // KeyedEncoderGLib uses a GVariant dictionary, so check if the given data is a valid GVariant dictionary.
114 GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new(data, size));
115 GRefPtr<GVariant> variant = g_variant_new_from_bytes(G_VARIANT_TYPE("a{sv}"), bytes.get(), FALSE);
116 return g_variant_is_normal_form(variant.get());
117#else
118 UNUSED_PARAM(data);
119 UNUSED_PARAM(size);
120#endif
121 return false;
122}
123
124
125/*
126The IDBKeyData serialization format is as follows:
127[1 byte version header][Key Buffer]
128
129The Key Buffer serialization format is as follows:
130[1 byte key type][Type specific data]
131
132Type specific serialization formats are as follows for each of the types:
133Min:
134[0 bytes]
135
136Number:
137[8 bytes representing a double encoded in little endian]
138
139Date:
140[8 bytes representing a double encoded in little endian]
141
142String:
143[4 bytes representing string "length" in little endian]["length" number of 2-byte pairs representing ECMAScript 16-bit code units]
144
145Binary:
146[8 bytes representing the "size" of the binary blob]["size" bytes]
147
148Array:
149[8 bytes representing the "length" of the key array]["length" individual Key Buffer entries]
150
151Max:
152[0 bytes]
153*/
154
155static const uint8_t SIDBKeyVersion = 0x00;
156enum class SIDBKeyType : uint8_t {
157 Min = 0x00,
158 Number = 0x20,
159 Date = 0x40,
160 String = 0x60,
161 Binary = 0x80,
162 Array = 0xA0,
163 Max = 0xFF,
164};
165
166static SIDBKeyType serializedTypeForKeyType(IndexedDB::KeyType type)
167{
168 switch (type) {
169 case IndexedDB::KeyType::Min:
170 return SIDBKeyType::Min;
171 case IndexedDB::KeyType::Number:
172 return SIDBKeyType::Number;
173 case IndexedDB::KeyType::Date:
174 return SIDBKeyType::Date;
175 case IndexedDB::KeyType::String:
176 return SIDBKeyType::String;
177 case IndexedDB::KeyType::Binary:
178 return SIDBKeyType::Binary;
179 case IndexedDB::KeyType::Array:
180 return SIDBKeyType::Array;
181 case IndexedDB::KeyType::Max:
182 return SIDBKeyType::Max;
183 case IndexedDB::KeyType::Invalid:
184 RELEASE_ASSERT_NOT_REACHED();
185 };
186
187 RELEASE_ASSERT_NOT_REACHED();
188}
189
190#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS)
191template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
192{
193 for (unsigned i = 0; i < sizeof(T); i++) {
194 buffer.append(value & 0xFF);
195 value >>= 8;
196 }
197}
198
199template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
200{
201 if (ptr > end - sizeof(value))
202 return false;
203
204 value = 0;
205 for (size_t i = 0; i < sizeof(T); i++)
206 value += ((T)*ptr++) << (i * 8);
207 return true;
208}
209#else
210template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
211{
212 buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value));
213}
214
215template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
216{
217 if (ptr > end - sizeof(value))
218 return false;
219
220 value = *reinterpret_cast<const T*>(ptr);
221 ptr += sizeof(T);
222
223 return true;
224}
225#endif
226
227static void writeDouble(Vector<char>& data, double d)
228{
229 writeLittleEndian(data, *reinterpret_cast<uint64_t*>(&d));
230}
231
232static bool readDouble(const uint8_t*& data, const uint8_t* end, double& d)
233{
234 return readLittleEndian(data, end, *reinterpret_cast<uint64_t*>(&d));
235}
236
237static void encodeKey(Vector<char>& data, const IDBKeyData& key)
238{
239 SIDBKeyType type = serializedTypeForKeyType(key.type());
240 data.append(static_cast<char>(type));
241
242 switch (type) {
243 case SIDBKeyType::Number:
244 writeDouble(data, key.number());
245 break;
246 case SIDBKeyType::Date:
247 writeDouble(data, key.date());
248 break;
249 case SIDBKeyType::String: {
250 auto string = key.string();
251 uint32_t length = string.length();
252 writeLittleEndian(data, length);
253
254 for (size_t i = 0; i < length; ++i)
255 writeLittleEndian(data, string[i]);
256
257 break;
258 }
259 case SIDBKeyType::Binary: {
260 auto& buffer = key.binary();
261 uint64_t size = buffer.size();
262 writeLittleEndian(data, size);
263
264 auto* bufferData = buffer.data();
265 ASSERT(bufferData || !size);
266 if (bufferData)
267 data.append(bufferData->data(), bufferData->size());
268
269 break;
270 }
271 case SIDBKeyType::Array: {
272 auto& array = key.array();
273 uint64_t size = array.size();
274 writeLittleEndian(data, size);
275 for (auto& key : array)
276 encodeKey(data, key);
277
278 break;
279 }
280 case SIDBKeyType::Min:
281 case SIDBKeyType::Max:
282 break;
283 }
284}
285
286RefPtr<SharedBuffer> serializeIDBKeyData(const IDBKeyData& key)
287{
288 Vector<char> data;
289 data.append(SIDBKeyVersion);
290
291 encodeKey(data, key);
292 return SharedBuffer::create(WTFMove(data));
293}
294
295static bool decodeKey(const uint8_t*& data, const uint8_t* end, IDBKeyData& result)
296{
297 if (!data || data >= end)
298 return false;
299
300 SIDBKeyType type = static_cast<SIDBKeyType>(data++[0]);
301 switch (type) {
302 case SIDBKeyType::Min:
303 result = IDBKeyData::minimum();
304 return true;
305 case SIDBKeyType::Max:
306 result = IDBKeyData::maximum();
307 return true;
308 case SIDBKeyType::Number: {
309 double d;
310 if (!readDouble(data, end, d))
311 return false;
312
313 result.setNumberValue(d);
314 return true;
315 }
316 case SIDBKeyType::Date: {
317 double d;
318 if (!readDouble(data, end, d))
319 return false;
320
321 result.setDateValue(d);
322 return true;
323 }
324 case SIDBKeyType::String: {
325 uint32_t length;
326 if (!readLittleEndian(data, end, length))
327 return false;
328
329 if (static_cast<uint64_t>(end - data) < length * 2)
330 return false;
331
332 Vector<UChar> buffer;
333 buffer.reserveInitialCapacity(length);
334 for (size_t i = 0; i < length; i++) {
335 uint16_t ch;
336 if (!readLittleEndian(data, end, ch))
337 return false;
338 buffer.uncheckedAppend(ch);
339 }
340
341 result.setStringValue(String::adopt(WTFMove(buffer)));
342
343 return true;
344 }
345 case SIDBKeyType::Binary: {
346 uint64_t size64;
347 if (!readLittleEndian(data, end, size64))
348 return false;
349
350 if (static_cast<uint64_t>(end - data) < size64)
351 return false;
352
353 if (size64 > std::numeric_limits<size_t>::max())
354 return false;
355
356 size_t size = static_cast<size_t>(size64);
357 Vector<uint8_t> dataVector;
358
359 dataVector.append(data, size);
360 data += size;
361
362 result.setBinaryValue(ThreadSafeDataBuffer::create(WTFMove(dataVector)));
363 return true;
364 }
365 case SIDBKeyType::Array: {
366 uint64_t size64;
367 if (!readLittleEndian(data, end, size64))
368 return false;
369
370 if (size64 > std::numeric_limits<size_t>::max())
371 return false;
372
373 size_t size = static_cast<size_t>(size64);
374 Vector<IDBKeyData> array;
375 array.reserveInitialCapacity(size);
376
377 for (size_t i = 0; i < size; ++i) {
378 IDBKeyData keyData;
379 if (!decodeKey(data, end, keyData))
380 return false;
381
382 ASSERT(keyData.isValid());
383 array.uncheckedAppend(WTFMove(keyData));
384 }
385
386 result.setArrayValue(array);
387
388 return true;
389 }
390 default:
391 LOG_ERROR("decodeKey encountered unexpected type: %i", (int)type);
392 return false;
393 }
394}
395
396bool deserializeIDBKeyData(const uint8_t* data, size_t size, IDBKeyData& result)
397{
398 if (!data || !size)
399 return false;
400
401 if (isLegacySerializedIDBKeyData(data, size)) {
402 auto decoder = KeyedDecoder::decoder(data, size);
403 return IDBKeyData::decode(*decoder, result);
404 }
405
406 // Verify this is a SerializedIDBKey version we understand.
407 const uint8_t* current = data;
408 const uint8_t* end = data + size;
409 if (current++[0] != SIDBKeyVersion)
410 return false;
411
412 if (decodeKey(current, end, result)) {
413 // Even if we successfully decoded a key, the deserialize is only successful
414 // if we actually consumed all input data.
415 return current == end;
416 }
417
418 return false;
419}
420
421} // namespace WebCore
422
423#endif // ENABLE(INDEXED_DATABASE)
424