1/*
2 * Copyright (C) 2016 Metrological Group B.V.
3 * Copyright (C) 2016 Igalia S.L.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials provided
14 * with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "CDMClearKey.h"
31
32#if ENABLE(ENCRYPTED_MEDIA)
33
34#include "CDMKeySystemConfiguration.h"
35#include "CDMRestrictions.h"
36#include "CDMSessionType.h"
37#include "SharedBuffer.h"
38#include <wtf/JSONValues.h>
39#include <wtf/MainThread.h>
40#include <wtf/NeverDestroyed.h>
41#include <wtf/text/Base64.h>
42
43namespace WebCore {
44
45// ClearKey CENC SystemID.
46// https://www.w3.org/TR/eme-initdata-cenc/#common-system
47const uint8_t clearKeyCencSystemId[] = { 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b };
48const unsigned clearKeyCencSystemIdSize = sizeof(clearKeyCencSystemId);
49const unsigned keyIdSize = 16;
50
51class ClearKeyState {
52 using KeyStore = HashMap<String, Vector<CDMInstanceClearKey::Key>>;
53
54public:
55 static ClearKeyState& singleton();
56
57 KeyStore& keys() { return m_keys; }
58
59private:
60 friend class NeverDestroyed<ClearKeyState>;
61 ClearKeyState();
62 KeyStore m_keys;
63};
64
65ClearKeyState& ClearKeyState::singleton()
66{
67 static NeverDestroyed<ClearKeyState> s_state;
68 return s_state;
69}
70
71ClearKeyState::ClearKeyState() = default;
72
73static RefPtr<JSON::Object> parseJSONObject(const SharedBuffer& buffer)
74{
75 // Fail on large buffers whose size doesn't fit into a 32-bit unsigned integer.
76 size_t size = buffer.size();
77 if (size > std::numeric_limits<unsigned>::max())
78 return nullptr;
79
80 // Parse the buffer contents as JSON, returning the root object (if any).
81 String json { buffer.data(), static_cast<unsigned>(size) };
82 RefPtr<JSON::Value> value;
83 RefPtr<JSON::Object> object;
84 if (!JSON::Value::parseJSON(json, value) || !value->asObject(object))
85 return nullptr;
86
87 return object;
88}
89
90static Optional<Vector<CDMInstanceClearKey::Key>> parseLicenseFormat(const JSON::Object& root)
91{
92 // If the 'keys' key is present in the root object, parse the JSON further
93 // according to the specified 'license' format.
94 auto it = root.find("keys");
95 if (it == root.end())
96 return WTF::nullopt;
97
98 // Retrieve the keys array.
99 RefPtr<JSON::Array> keysArray;
100 if (!it->value->asArray(keysArray))
101 return WTF::nullopt;
102
103 Vector<CDMInstanceClearKey::Key> decodedKeys;
104 bool validFormat = std::all_of(keysArray->begin(), keysArray->end(),
105 [&decodedKeys] (const auto& value) {
106 RefPtr<JSON::Object> keyObject;
107 if (!value->asObject(keyObject))
108 return false;
109
110 String keyType;
111 if (!keyObject->getString("kty", keyType) || !equalLettersIgnoringASCIICase(keyType, "oct"))
112 return false;
113
114 String keyID, keyValue;
115 if (!keyObject->getString("kid", keyID) || !keyObject->getString("k", keyValue))
116 return false;
117
118 Vector<char> keyIDData, keyValueData;
119 if (!WTF::base64URLDecode(keyID, { keyIDData }) || !WTF::base64URLDecode(keyValue, { keyValueData }))
120 return false;
121
122 decodedKeys.append({ CDMInstanceSession::KeyStatus::Usable, SharedBuffer::create(WTFMove(keyIDData)), SharedBuffer::create(WTFMove(keyValueData)) });
123 return true;
124 });
125 if (!validFormat)
126 return WTF::nullopt;
127 return decodedKeys;
128}
129
130static bool parseLicenseReleaseAcknowledgementFormat(const JSON::Object& root)
131{
132 // If the 'kids' key is present in the root object, parse the JSON further
133 // according to the specified 'license release acknowledgement' format.
134 auto it = root.find("kids");
135 if (it == root.end())
136 return false;
137
138 // Retrieve the kids array.
139 RefPtr<JSON::Array> kidsArray;
140 if (!it->value->asArray(kidsArray))
141 return false;
142
143 // FIXME: Return the key IDs and validate them.
144 return true;
145}
146
147// https://www.w3.org/TR/eme-initdata-cenc/#common-system
148// 4.1 Definition
149// The SystemID is 1077efec-c0b2-4d02-ace3-3c1e52e2fb4b.
150// The PSSH box format is as follows. It follows version 1 of the 'pssh' box as defined in [CENC].
151// pssh = [
152// 0x00, 0x00, 0x00, 0x4c, 0x70, 0x73, 0x73, 0x68, // BMFF box header (76 bytes, 'pssh')
153// 0x01, 0x00, 0x00, 0x00, // Full box header (version = 1, flags = 0)
154// 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // SystemID
155// 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
156// 0x00, 0x00, 0x00, 0x02, // KidCount (2)
157// 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // First KID ("0123456789012345")
158// 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
159// 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, // Second KID ("ABCDEFGHIJKLMNOP")
160// 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
161// 0x00, 0x00, 0x00, 0x00, // Size of Data (0)
162// ];
163
164// This function extracts the KeyIds count and the location of the first KeyId in initData buffer.
165static std::pair<unsigned, unsigned> extractKeyidsLocationFromCencInitData(const SharedBuffer& initData)
166{
167 std::pair<unsigned, unsigned> keyIdsMap(0, 0);
168
169 // Check the initData size.
170 if (initData.isEmpty() || initData.size() > std::numeric_limits<unsigned>::max())
171 return keyIdsMap;
172
173 const char* data = initData.data();
174 unsigned initDataSize = initData.size();
175 unsigned index = 0;
176 unsigned psshSize = 0;
177
178 // Search in the concatenated or the simple InitData, the ClearKey PSSH.
179 bool foundPssh = false;
180 while (true) {
181
182 // Check the overflow InitData.
183 if (index + 12 + clearKeyCencSystemIdSize >= initDataSize)
184 return keyIdsMap;
185
186 psshSize = data[index + 2] * 256 + data[index + 3];
187
188 // Check the pssh size
189 if (!psshSize)
190 return keyIdsMap;
191
192 // 12 = BMFF box header + Full box header.
193 if (!memcmp(&data[index + 12], clearKeyCencSystemId, clearKeyCencSystemIdSize)) {
194 foundPssh = true;
195 break;
196 }
197 index += psshSize;
198 }
199
200 // Check if the InitData contains the ClearKey PSSH.
201 if (!foundPssh)
202 return keyIdsMap;
203
204 index += (12 + clearKeyCencSystemIdSize); // 12 (BMFF box header + Full box header) + SystemID size.
205
206 // Check the overflow.
207 if (index + 3 >= initDataSize)
208 return keyIdsMap;
209
210 keyIdsMap.first = data[index + 3]; // Read the KeyIdsCount.
211 index += 4; // KeyIdsCount size.
212
213 // Check the overflow.
214 if ((index + (keyIdsMap.first * keyIdSize)) >= initDataSize)
215 return keyIdsMap;
216
217 keyIdsMap.second = index; // The location of the first KeyId in initData.
218
219 return keyIdsMap;
220}
221
222// This function checks if the initData sharedBuffer is a valid CENC initData.
223static bool isCencInitData(const SharedBuffer& initData)
224{
225 std::pair<unsigned, unsigned> keyIdsMap = extractKeyidsLocationFromCencInitData(initData);
226 return ((keyIdsMap.first) && (keyIdsMap.second));
227}
228
229static Ref<SharedBuffer> extractKeyidsFromCencInitData(const SharedBuffer& initData)
230{
231 Ref<SharedBuffer> keyIds = SharedBuffer::create();
232
233 std::pair<unsigned, unsigned> keyIdsMap = extractKeyidsLocationFromCencInitData(initData);
234 unsigned keyIdCount = keyIdsMap.first;
235 unsigned index = keyIdsMap.second;
236
237 // Check if initData is a valid CENC initData.
238 if (!keyIdCount || !index)
239 return keyIds;
240
241 const char* data = initData.data();
242
243 auto object = JSON::Object::create();
244 auto keyIdsArray = JSON::Array::create();
245
246 // Read the KeyId
247 // 9.1.3 License Request Format
248 // This section describes the format of the license request provided to the application via the message attribute of the message event.
249 // The format is a JSON object containing the following members:
250 // "kids"
251 // An array of key IDs. Each element of the array is the base64url encoding of the octet sequence containing the key ID value.
252 for (unsigned i = 0; i < keyIdCount; i++) {
253 String keyId = WTF::base64URLEncode(&data[index], keyIdSize);
254 keyIdsArray->pushString(keyId);
255 index += keyIdSize;
256 }
257
258 object->setArray("kids", WTFMove(keyIdsArray));
259 CString jsonData = object->toJSONString().utf8();
260 keyIds->append(jsonData.data(), jsonData.length());
261 return keyIds;
262}
263
264static Ref<SharedBuffer> extractKeyIdFromWebMInitData(const SharedBuffer& initData)
265{
266 Ref<SharedBuffer> keyIds = SharedBuffer::create();
267
268 // Check if initData is a valid WebM initData.
269 if (initData.isEmpty() || initData.size() > std::numeric_limits<unsigned>::max())
270 return keyIds;
271
272 auto object = JSON::Object::create();
273 auto keyIdsArray = JSON::Array::create();
274
275 // Read the KeyId
276 // 9.1.3 License Request Format
277 // This section describes the format of the license request provided to the application via the message attribute of the message event.
278 // The format is a JSON object containing the following members:
279 // "kids"
280 // An array of key IDs. Each element of the array is the base64url encoding of the octet sequence containing the key ID value.
281 String keyId = WTF::base64URLEncode(initData.data(), initData.size());
282 keyIdsArray->pushString(keyId);
283
284 object->setArray("kids", WTFMove(keyIdsArray));
285 CString jsonData = object->toJSONString().utf8();
286 keyIds->append(jsonData.data(), jsonData.length());
287 return keyIds;
288}
289
290CDMFactoryClearKey& CDMFactoryClearKey::singleton()
291{
292 static NeverDestroyed<CDMFactoryClearKey> s_factory;
293 return s_factory;
294}
295
296CDMFactoryClearKey::CDMFactoryClearKey() = default;
297CDMFactoryClearKey::~CDMFactoryClearKey() = default;
298
299std::unique_ptr<CDMPrivate> CDMFactoryClearKey::createCDM(const String& keySystem)
300{
301#ifdef NDEBUG
302 UNUSED_PARAM(keySystem);
303#else
304 ASSERT(supportsKeySystem(keySystem));
305#endif
306 return std::make_unique<CDMPrivateClearKey>();
307}
308
309bool CDMFactoryClearKey::supportsKeySystem(const String& keySystem)
310{
311 // `org.w3.clearkey` is the only supported key system.
312 return equalLettersIgnoringASCIICase(keySystem, "org.w3.clearkey");
313}
314
315CDMPrivateClearKey::CDMPrivateClearKey() = default;
316CDMPrivateClearKey::~CDMPrivateClearKey() = default;
317
318bool CDMPrivateClearKey::supportsInitDataType(const AtomicString& initDataType) const
319{
320 // `keyids` and 'cenc' are the only supported init data type.
321 return (equalLettersIgnoringASCIICase(initDataType, "keyids") || equalLettersIgnoringASCIICase(initDataType, "cenc") || equalLettersIgnoringASCIICase(initDataType, "webm"));
322}
323
324static bool containsPersistentLicenseType(const Vector<CDMSessionType>& types)
325{
326 return std::any_of(types.begin(), types.end(),
327 [] (auto& sessionType) { return sessionType == CDMSessionType::PersistentLicense; });
328}
329
330bool CDMPrivateClearKey::supportsConfiguration(const CDMKeySystemConfiguration& configuration) const
331{
332 // Reject any configuration that marks distinctive identifier as required.
333 if (configuration.distinctiveIdentifier == CDMRequirement::Required)
334 return false;
335
336 // Reject any configuration that marks persistent state as required, unless
337 // the 'persistent-license' session type has to be supported.
338 if (configuration.persistentState == CDMRequirement::Required && !containsPersistentLicenseType(configuration.sessionTypes))
339 return false;
340
341 return true;
342}
343
344bool CDMPrivateClearKey::supportsConfigurationWithRestrictions(const CDMKeySystemConfiguration& configuration, const CDMRestrictions& restrictions) const
345{
346 // Reject any configuration that marks distincitive identifier as required, or that marks
347 // distinctive identifier as optional even when restrictions mark it as denied.
348 if ((configuration.distinctiveIdentifier == CDMRequirement::Optional && restrictions.distinctiveIdentifierDenied)
349 || configuration.distinctiveIdentifier == CDMRequirement::Required)
350 return false;
351
352 // Reject any configuration that marks persistent state as optional even when
353 // restrictions mark it as denied.
354 if (configuration.persistentState == CDMRequirement::Optional && restrictions.persistentStateDenied)
355 return false;
356
357 // Reject any configuration that marks persistent state as required, unless
358 // the 'persistent-license' session type has to be supported.
359 if (configuration.persistentState == CDMRequirement::Required && !containsPersistentLicenseType(configuration.sessionTypes))
360 return false;
361
362 return true;
363}
364
365bool CDMPrivateClearKey::supportsSessionTypeWithConfiguration(CDMSessionType& sessionType, const CDMKeySystemConfiguration& configuration) const
366{
367 // Only support the 'temporary' and 'persistent-license' session types.
368 if (sessionType != CDMSessionType::Temporary && sessionType != CDMSessionType::PersistentLicense)
369 return false;
370 return supportsConfiguration(configuration);
371}
372
373bool CDMPrivateClearKey::supportsRobustness(const String& robustness) const
374{
375 // Only empty `robustness` string is supported.
376 return robustness.isEmpty();
377}
378
379CDMRequirement CDMPrivateClearKey::distinctiveIdentifiersRequirement(const CDMKeySystemConfiguration&, const CDMRestrictions& restrictions) const
380{
381 // Distinctive identifier is not allowed if it's been denied, otherwise it's optional.
382 if (restrictions.distinctiveIdentifierDenied)
383 return CDMRequirement::NotAllowed;
384 return CDMRequirement::Optional;
385}
386
387CDMRequirement CDMPrivateClearKey::persistentStateRequirement(const CDMKeySystemConfiguration&, const CDMRestrictions& restrictions) const
388{
389 // Persistent state is not allowed if it's been denied, otherwise it's optional.
390 if (restrictions.persistentStateDenied)
391 return CDMRequirement::NotAllowed;
392 return CDMRequirement::Optional;
393}
394
395bool CDMPrivateClearKey::distinctiveIdentifiersAreUniquePerOriginAndClearable(const CDMKeySystemConfiguration&) const
396{
397 return false;
398}
399
400RefPtr<CDMInstance> CDMPrivateClearKey::createInstance()
401{
402 return adoptRef(new CDMInstanceClearKey);
403}
404
405void CDMPrivateClearKey::loadAndInitialize()
406{
407 // No-op.
408}
409
410bool CDMPrivateClearKey::supportsServerCertificates() const
411{
412 // Server certificates are not supported.
413 return false;
414}
415
416bool CDMPrivateClearKey::supportsSessions() const
417{
418 // Sessions are supported.
419 return true;
420}
421
422bool CDMPrivateClearKey::supportsInitData(const AtomicString& initDataType, const SharedBuffer& initData) const
423{
424 // Validate the initData buffer as an JSON object in keyids case.
425 if (equalLettersIgnoringASCIICase(initDataType, "keyids") && parseJSONObject(initData))
426 return true;
427
428 // Validate the initData buffer as CENC initData.
429 if (equalLettersIgnoringASCIICase(initDataType, "cenc") && isCencInitData(initData))
430 return true;
431
432 // Validate the initData buffer as WebM initData.
433 if (equalLettersIgnoringASCIICase(initDataType, "webm") && !initData.isEmpty())
434 return true;
435
436 return false;
437}
438
439RefPtr<SharedBuffer> CDMPrivateClearKey::sanitizeResponse(const SharedBuffer& response) const
440{
441 // Validate the response buffer as an JSON object.
442 if (!parseJSONObject(response))
443 return nullptr;
444
445 return response.copy();
446}
447
448Optional<String> CDMPrivateClearKey::sanitizeSessionId(const String& sessionId) const
449{
450 // Validate the session ID string as an 32-bit integer.
451 bool ok;
452 sessionId.toUIntStrict(&ok);
453 if (!ok)
454 return WTF::nullopt;
455 return sessionId;
456}
457
458CDMInstanceClearKey::CDMInstanceClearKey()
459{
460}
461
462CDMInstanceClearKey::~CDMInstanceClearKey() = default;
463
464CDMInstance::SuccessValue CDMInstanceClearKey::initializeWithConfiguration(const CDMKeySystemConfiguration&)
465{
466 // No-op.
467 return Succeeded;
468}
469
470CDMInstance::SuccessValue CDMInstanceClearKey::setDistinctiveIdentifiersAllowed(bool allowed)
471{
472 // Reject setting distinctive identifiers as allowed.
473 return !allowed ? Succeeded : Failed;
474}
475
476CDMInstance::SuccessValue CDMInstanceClearKey::setPersistentStateAllowed(bool allowed)
477{
478 // Reject setting persistent state as allowed.
479 return !allowed ? Succeeded : Failed;
480}
481
482CDMInstance::SuccessValue CDMInstanceClearKey::setServerCertificate(Ref<SharedBuffer>&&)
483{
484 // Reject setting any server certificate.
485 return Failed;
486}
487
488CDMInstance::SuccessValue CDMInstanceClearKey::setStorageDirectory(const String& storageDirectory)
489{
490 // Reject any persistent state storage.
491 return storageDirectory.isEmpty() ? Succeeded : Failed;
492}
493
494const String& CDMInstanceClearKey::keySystem() const
495{
496 static const NeverDestroyed<String> s_keySystem { MAKE_STATIC_STRING_IMPL("org.w3.clearkey") };
497
498 return s_keySystem;
499}
500
501RefPtr<CDMInstanceSession> CDMInstanceClearKey::createSession()
502{
503 return adoptRef(new CDMInstanceSessionClearKey());
504}
505
506const Vector<CDMInstanceClearKey::Key> CDMInstanceClearKey::keys() const
507{
508 // Return the keys of all sessions.
509 Vector<CDMInstanceClearKey::Key> allKeys { };
510 auto locker = holdLock(m_keysMutex);
511 size_t initialCapacity = 0;
512 for (auto& key : ClearKeyState::singleton().keys().values())
513 initialCapacity += key.size();
514 allKeys.reserveInitialCapacity(initialCapacity);
515
516 for (auto& key : ClearKeyState::singleton().keys().values())
517 allKeys.appendVector(key);
518
519 return allKeys;
520}
521
522void CDMInstanceSessionClearKey::requestLicense(LicenseType, const AtomicString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback&& callback)
523{
524 static uint32_t s_sessionIdValue = 0;
525 ++s_sessionIdValue;
526
527 if (equalLettersIgnoringASCIICase(initDataType, "cenc"))
528 initData = extractKeyidsFromCencInitData(initData.get());
529
530 if (equalLettersIgnoringASCIICase(initDataType, "webm"))
531 initData = extractKeyIdFromWebMInitData(initData.get());
532
533 callOnMainThread(
534 [weakThis = makeWeakPtr(*this), callback = WTFMove(callback), initData = WTFMove(initData), sessionIdValue = s_sessionIdValue]() mutable {
535 if (!weakThis)
536 return;
537
538 callback(WTFMove(initData), String::number(sessionIdValue), false, Succeeded);
539 });
540}
541
542void CDMInstanceSessionClearKey::updateLicense(const String& sessionId, LicenseType, const SharedBuffer& response, LicenseUpdateCallback&& callback)
543{
544 // Use a helper functor that schedules the callback dispatch, avoiding
545 // duplicated callOnMainThread() calls.
546 auto dispatchCallback =
547 [this, &callback](bool sessionWasClosed, Optional<KeyStatusVector>&& changedKeys, SuccessValue succeeded) {
548 callOnMainThread(
549 [weakThis = makeWeakPtr(*this), callback = WTFMove(callback), sessionWasClosed, changedKeys = WTFMove(changedKeys), succeeded] () mutable {
550 if (!weakThis)
551 return;
552
553 callback(sessionWasClosed, WTFMove(changedKeys), WTF::nullopt, WTF::nullopt, succeeded);
554 });
555 };
556
557 // Parse the response buffer as an JSON object.
558 RefPtr<JSON::Object> root = parseJSONObject(response);
559 if (!root) {
560 dispatchCallback(false, WTF::nullopt, SuccessValue::Failed);
561 return;
562 }
563
564 // Parse the response using 'license' formatting, if possible.
565 if (auto decodedKeys = parseLicenseFormat(*root)) {
566 // Retrieve the target Vector of Key objects for this session.
567 auto& keyVector = ClearKeyState::singleton().keys().ensure(sessionId, [] { return Vector<CDMInstanceClearKey::Key> { }; }).iterator->value;
568
569 // For each decoded key, find an existing item for the decoded key's ID. If none exist,
570 // the key is decoded. Otherwise, the key is updated in case there's a mismatch between
571 // the size or data of the existing and proposed key.
572 bool keysChanged = false;
573 for (auto& key : *decodedKeys) {
574 auto it = std::find_if(keyVector.begin(), keyVector.end(),
575 [&key] (const CDMInstanceClearKey::Key& containedKey) {
576 return containedKey.keyIDData->size() == key.keyIDData->size()
577 && !std::memcmp(containedKey.keyIDData->data(), key.keyIDData->data(), containedKey.keyIDData->size());
578 });
579 if (it != keyVector.end()) {
580 auto& existingKey = it->keyValueData;
581 auto& proposedKey = key.keyValueData;
582
583 // Update the existing Key if it differs from the proposed key in key value.
584 if (existingKey->size() != proposedKey->size() || std::memcmp(existingKey->data(), proposedKey->data(), existingKey->size())) {
585 *it = WTFMove(key);
586 keysChanged = true;
587 }
588 } else {
589 // In case a Key for this key ID doesn't exist yet, append the new one to keyVector.
590 keyVector.append(WTFMove(key));
591 keysChanged = true;
592 }
593 }
594
595 // In case of changed keys, we have to provide a KeyStatusVector of all the keys for
596 // this session.
597 Optional<KeyStatusVector> changedKeys;
598 if (keysChanged) {
599 // First a helper Vector is constructed, cotaining pairs of SharedBuffer RefPtrs
600 // representint key ID data, and the corresponding key statuses.
601 // We can't use KeyStatusVector here because this Vector has to be sorted, which
602 // is not possible to do on Ref<> objects.
603 Vector<std::pair<RefPtr<SharedBuffer>, KeyStatus>> keys;
604 keys.reserveInitialCapacity(keyVector.size());
605 for (auto& it : keyVector)
606 keys.uncheckedAppend(std::pair<RefPtr<SharedBuffer>, KeyStatus> { it.keyIDData, it.status });
607
608 // Sort first by size, second by data.
609 std::sort(keys.begin(), keys.end(),
610 [] (const auto& a, const auto& b) {
611 if (a.first->size() != b.first->size())
612 return a.first->size() < b.first->size();
613
614 return std::memcmp(a.first->data(), b.first->data(), a.first->size()) < 0;
615 });
616
617 // Finally construct the mirroring KeyStatusVector object and move it into the
618 // Optional<> object that will be passed to the callback.
619 KeyStatusVector keyStatusVector;
620 keyStatusVector.reserveInitialCapacity(keys.size());
621 for (auto& it : keys)
622 keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *it.first, it.second });
623
624 changedKeys = WTFMove(keyStatusVector);
625 }
626
627 dispatchCallback(false, WTFMove(changedKeys), SuccessValue::Succeeded);
628 return;
629 }
630
631 // Parse the response using 'license release acknowledgement' formatting, if possible.
632 if (parseLicenseReleaseAcknowledgementFormat(*root)) {
633 // FIXME: Retrieve the key ID information and use it to validate the keys for this sessionId.
634 ClearKeyState::singleton().keys().remove(sessionId);
635 dispatchCallback(true, WTF::nullopt, SuccessValue::Succeeded);
636 return;
637 }
638
639 // Bail in case no format was recognized.
640 dispatchCallback(false, WTF::nullopt, SuccessValue::Failed);
641}
642
643void CDMInstanceSessionClearKey::loadSession(LicenseType, const String& sessionId, const String&, LoadSessionCallback&& callback)
644{
645 // Use a helper functor that schedules the callback dispatch, avoiding duplicated callOnMainThread() calls.
646 auto dispatchCallback =
647 [this, &callback](Optional<KeyStatusVector>&& existingKeys, SuccessValue success, SessionLoadFailure loadFailure) {
648 callOnMainThread(
649 [weakThis = makeWeakPtr(*this), callback = WTFMove(callback), existingKeys = WTFMove(existingKeys), success, loadFailure]() mutable {
650 if (!weakThis)
651 return;
652
653 callback(WTFMove(existingKeys), WTF::nullopt, WTF::nullopt, success, loadFailure);
654 });
655 };
656
657 // Construct the KeyStatusVector object, representing all the known keys for this session.
658 KeyStatusVector keyStatusVector;
659 {
660 auto& keys = ClearKeyState::singleton().keys();
661 auto it = keys.find(sessionId);
662 if (it == keys.end()) {
663 dispatchCallback(WTF::nullopt, Failed, SessionLoadFailure::NoSessionData);
664 return;
665 }
666
667 auto& keyVector = it->value;
668 keyStatusVector.reserveInitialCapacity(keyVector.size());
669 for (auto& key : keyVector)
670 keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *key.keyIDData, key.status });
671 }
672
673 dispatchCallback(WTFMove(keyStatusVector), Succeeded, SessionLoadFailure::None);
674}
675
676void CDMInstanceSessionClearKey::closeSession(const String&, CloseSessionCallback&& callback)
677{
678 callOnMainThread(
679 [weakThis = makeWeakPtr(*this), callback = WTFMove(callback)] () mutable {
680 if (!weakThis)
681 return;
682
683 callback();
684 });
685}
686
687void CDMInstanceSessionClearKey::removeSessionData(const String& sessionId, LicenseType, RemoveSessionDataCallback&& callback)
688{
689 // Use a helper functor that schedules the callback dispatch, avoiding duplicated callOnMainThread() calls.
690 auto dispatchCallback =
691 [this, &callback](KeyStatusVector&& keyStatusVector, Optional<Ref<SharedBuffer>>&& message, SuccessValue success) {
692 callOnMainThread(
693 [weakThis = makeWeakPtr(*this), callback = WTFMove(callback), keyStatusVector = WTFMove(keyStatusVector), message = WTFMove(message), success]() mutable {
694 if (!weakThis)
695 return;
696
697 callback(WTFMove(keyStatusVector), WTFMove(message), success);
698 });
699 };
700
701 // Construct the KeyStatusVector object, representing released keys, and the message in the
702 // 'license release' format.
703 KeyStatusVector keyStatusVector;
704 RefPtr<SharedBuffer> message;
705 {
706 // Retrieve information for the given session ID, bailing if none is found.
707 auto& keys = ClearKeyState::singleton().keys();
708 auto it = keys.find(sessionId);
709 if (it == keys.end()) {
710 dispatchCallback(KeyStatusVector { }, WTF::nullopt, SuccessValue::Failed);
711 return;
712 }
713
714 // Retrieve the Key vector, containing all the keys for this session, and
715 // then remove the key map entry for this session.
716 auto keyVector = WTFMove(it->value);
717 keys.remove(it);
718
719 // Construct the KeyStatusVector object, pairing key IDs with the 'released' status.
720 keyStatusVector.reserveInitialCapacity(keyVector.size());
721 for (auto& key : keyVector)
722 keyStatusVector.uncheckedAppend(std::pair<Ref<SharedBuffer>, KeyStatus> { *key.keyIDData, KeyStatus::Released });
723
724 // Construct JSON that represents the 'license release' format, creating a 'kids' array
725 // of base64URL-encoded key IDs for all keys that were associated with this session.
726 auto rootObject = JSON::Object::create();
727 {
728 auto array = JSON::Array::create();
729 for (auto& key : keyVector) {
730 ASSERT(key.keyIDData->size() <= std::numeric_limits<unsigned>::max());
731 array->pushString(WTF::base64URLEncode(key.keyIDData->data(), static_cast<unsigned>(key.keyIDData->size())));
732 }
733 rootObject->setArray("kids", WTFMove(array));
734 }
735
736 // Copy the JSON data into a SharedBuffer object.
737 String messageString = rootObject->toJSONString();
738 CString messageCString = messageString.utf8();
739 message = SharedBuffer::create(messageCString.data(), messageCString.length());
740 }
741
742 dispatchCallback(WTFMove(keyStatusVector), Ref<SharedBuffer>(*message), SuccessValue::Succeeded);
743}
744
745void CDMInstanceSessionClearKey::storeRecordOfKeyUsage(const String&)
746{
747}
748
749} // namespace WebCore
750
751#endif // ENABLE(ENCRYPTED_MEDIA)
752