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