Line data Source code
1 : /*
2 : * If not stated otherwise in this file or this component's LICENSE file the
3 : * following copyright and licenses apply:
4 : *
5 : * Copyright 2022 Sky UK
6 : *
7 : * Licensed under the Apache License, Version 2.0 (the "License");
8 : * you may not use this file except in compliance with the License.
9 : * You may obtain a copy of the License at
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
13 : * Unless required by applicable law or agreed to in writing, software
14 : * distributed under the License is distributed on an "AS IS" BASIS,
15 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : * See the License for the specific language governing permissions and
17 : * limitations under the License.
18 : */
19 :
20 : #include <stdexcept>
21 :
22 : #include "MediaKeySession.h"
23 : #include "MediaKeysCommon.h"
24 : #include "RialtoServerLogging.h"
25 :
26 : namespace firebolt::rialto::server
27 : {
28 2 : std::shared_ptr<IMediaKeySessionFactory> IMediaKeySessionFactory::createFactory()
29 : {
30 2 : std::shared_ptr<IMediaKeySessionFactory> factory;
31 :
32 : try
33 : {
34 2 : factory = std::make_shared<MediaKeySessionFactory>();
35 : }
36 0 : catch (const std::exception &e)
37 : {
38 0 : RIALTO_SERVER_LOG_ERROR("Failed to create the media key session factory, reason: %s", e.what());
39 : }
40 :
41 2 : return factory;
42 : }
43 :
44 1 : std::unique_ptr<IMediaKeySession> MediaKeySessionFactory::createMediaKeySession(
45 : const std::string &keySystem, int32_t keySessionId, const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem,
46 : KeySessionType sessionType, std::weak_ptr<IMediaKeysClient> client, bool isLDL) const
47 : {
48 1 : std::unique_ptr<IMediaKeySession> mediaKeys;
49 : try
50 : {
51 2 : mediaKeys = std::make_unique<server::MediaKeySession>(keySystem, keySessionId, ocdmSystem, sessionType, client,
52 3 : isLDL, server::IMainThreadFactory::createFactory());
53 : }
54 0 : catch (const std::exception &e)
55 : {
56 0 : RIALTO_SERVER_LOG_ERROR("Failed to create the media key session, reason: %s", e.what());
57 : }
58 :
59 1 : return mediaKeys;
60 : }
61 :
62 53 : MediaKeySession::MediaKeySession(const std::string &keySystem, int32_t keySessionId,
63 : const firebolt::rialto::wrappers::IOcdmSystem &ocdmSystem, KeySessionType sessionType,
64 : std::weak_ptr<IMediaKeysClient> client, bool isLDL,
65 53 : const std::shared_ptr<IMainThreadFactory> &mainThreadFactory)
66 53 : : m_kKeySystem(keySystem), m_kKeySessionId(keySessionId), m_kSessionType(sessionType), m_mediaKeysClient(client),
67 53 : m_kIsLDL(isLDL), m_isSessionConstructed(false), m_isSessionClosed(false), m_licenseRequested(false),
68 159 : m_ongoingOcdmOperation(false), m_ocdmError(false)
69 : {
70 53 : RIALTO_SERVER_LOG_DEBUG("entry:");
71 :
72 53 : m_mainThread = mainThreadFactory->getMainThread();
73 53 : if (!m_mainThread)
74 : {
75 1 : throw std::runtime_error("Failed to get the main thread");
76 : }
77 52 : m_mainThreadClientId = m_mainThread->registerClient();
78 :
79 52 : m_ocdmSession = ocdmSystem.createSession(this);
80 52 : if (!m_ocdmSession)
81 : {
82 1 : throw std::runtime_error("Ocdm session could not be created");
83 : }
84 67 : }
85 :
86 153 : MediaKeySession::~MediaKeySession()
87 : {
88 51 : RIALTO_SERVER_LOG_DEBUG("entry:");
89 :
90 51 : if (m_isSessionConstructed)
91 : {
92 7 : if (!m_isSessionClosed)
93 : {
94 7 : if (MediaKeyErrorStatus::OK != closeKeySession())
95 : {
96 0 : RIALTO_SERVER_LOG_ERROR("Failed to close the key session");
97 : }
98 : }
99 7 : if (MediaKeyErrorStatus::OK != m_ocdmSession->destructSession())
100 : {
101 0 : RIALTO_SERVER_LOG_ERROR("Failed to destruct the key session");
102 : }
103 : }
104 :
105 51 : m_mainThread->unregisterClient(m_mainThreadClientId);
106 102 : }
107 :
108 9 : MediaKeyErrorStatus MediaKeySession::generateRequest(InitDataType initDataType, const std::vector<uint8_t> &initData)
109 : {
110 9 : RIALTO_SERVER_LOG_DEBUG("entry:");
111 : // Set the request flag for the onLicenseRequest callback
112 9 : m_licenseRequested = true;
113 :
114 : // Only construct session if it hasnt previously been constructed
115 9 : if (!m_isSessionConstructed)
116 : {
117 8 : initOcdmErrorChecking();
118 :
119 : MediaKeyErrorStatus status =
120 8 : m_ocdmSession->constructSession(m_kSessionType, initDataType, &initData[0], initData.size());
121 8 : if (MediaKeyErrorStatus::OK != status)
122 : {
123 1 : RIALTO_SERVER_LOG_ERROR("Failed to construct the key session");
124 1 : m_licenseRequested = false;
125 : }
126 : else
127 : {
128 7 : m_isSessionConstructed = true;
129 7 : if (isNetflixPlayreadyKeySystem())
130 : {
131 : // Ocdm-playready does not notify onProcessChallenge when complete.
132 : // Fetch the challenge manually.
133 3 : getChallenge();
134 : }
135 : }
136 :
137 8 : if ((checkForOcdmErrors("generateRequest")) && (MediaKeyErrorStatus::OK == status))
138 : {
139 1 : status = MediaKeyErrorStatus::FAIL;
140 : }
141 :
142 8 : return status;
143 : }
144 :
145 1 : return MediaKeyErrorStatus::OK;
146 : }
147 :
148 3 : void MediaKeySession::getChallenge()
149 : {
150 3 : RIALTO_SERVER_LOG_DEBUG("entry:");
151 3 : auto task = [&]()
152 : {
153 3 : uint32_t challengeSize = 0;
154 3 : MediaKeyErrorStatus status = m_ocdmSession->getChallengeData(m_kIsLDL, nullptr, &challengeSize);
155 3 : if (challengeSize == 0)
156 : {
157 1 : RIALTO_SERVER_LOG_ERROR("Failed to get the challenge data size, no onLicenseRequest will be generated");
158 2 : return;
159 : }
160 2 : std::vector<uint8_t> challenge(challengeSize, 0x00);
161 2 : status = m_ocdmSession->getChallengeData(m_kIsLDL, &challenge[0], &challengeSize);
162 2 : if (MediaKeyErrorStatus::OK != status)
163 : {
164 1 : RIALTO_SERVER_LOG_ERROR("Failed to get the challenge data, no onLicenseRequest will be generated");
165 1 : return;
166 : }
167 :
168 1 : std::string url;
169 1 : onProcessChallenge(url.c_str(), &challenge[0], challengeSize);
170 2 : };
171 3 : m_mainThread->enqueueTask(m_mainThreadClientId, task);
172 : }
173 :
174 3 : MediaKeyErrorStatus MediaKeySession::loadSession()
175 : {
176 3 : initOcdmErrorChecking();
177 :
178 3 : MediaKeyErrorStatus status = m_ocdmSession->load();
179 3 : if (MediaKeyErrorStatus::OK != status)
180 : {
181 1 : RIALTO_SERVER_LOG_ERROR("Failed to load the key session");
182 : }
183 :
184 3 : if ((checkForOcdmErrors("loadSession")) && (MediaKeyErrorStatus::OK == status))
185 : {
186 1 : status = MediaKeyErrorStatus::FAIL;
187 : }
188 :
189 3 : return status;
190 : }
191 :
192 5 : MediaKeyErrorStatus MediaKeySession::updateSession(const std::vector<uint8_t> &responseData)
193 : {
194 5 : initOcdmErrorChecking();
195 :
196 : MediaKeyErrorStatus status;
197 5 : if (isNetflixPlayreadyKeySystem())
198 : {
199 2 : status = m_ocdmSession->storeLicenseData(&responseData[0], responseData.size());
200 2 : if (MediaKeyErrorStatus::OK != status)
201 : {
202 1 : RIALTO_SERVER_LOG_ERROR("Failed to store the license data for the key session");
203 : }
204 : }
205 : else
206 : {
207 3 : status = m_ocdmSession->update(&responseData[0], responseData.size());
208 3 : if (MediaKeyErrorStatus::OK != status)
209 : {
210 1 : RIALTO_SERVER_LOG_ERROR("Failed to update the key session");
211 : }
212 : }
213 :
214 5 : if ((checkForOcdmErrors("updateSession")) && (MediaKeyErrorStatus::OK == status))
215 : {
216 1 : status = MediaKeyErrorStatus::FAIL;
217 : }
218 :
219 5 : return status;
220 : }
221 :
222 3 : MediaKeyErrorStatus MediaKeySession::decrypt(GstBuffer *encrypted, GstCaps *caps)
223 : {
224 3 : initOcdmErrorChecking();
225 :
226 3 : MediaKeyErrorStatus status = m_ocdmSession->decryptBuffer(encrypted, caps);
227 3 : if (MediaKeyErrorStatus::OK != status)
228 : {
229 1 : RIALTO_SERVER_LOG_ERROR("Failed to decrypt buffer");
230 : }
231 :
232 3 : if ((checkForOcdmErrors("decrypt")) && (MediaKeyErrorStatus::OK == status))
233 : {
234 1 : status = MediaKeyErrorStatus::FAIL;
235 : }
236 :
237 3 : return status;
238 : }
239 :
240 13 : MediaKeyErrorStatus MediaKeySession::closeKeySession()
241 : {
242 13 : initOcdmErrorChecking();
243 :
244 : MediaKeyErrorStatus status;
245 13 : if (isNetflixPlayreadyKeySystem())
246 : {
247 6 : if (MediaKeyErrorStatus::OK != m_ocdmSession->cancelChallengeData())
248 : {
249 1 : RIALTO_SERVER_LOG_WARN("Failed to cancel the challenge data for the key session");
250 : }
251 :
252 6 : if (MediaKeyErrorStatus::OK != m_ocdmSession->cleanDecryptContext())
253 : {
254 1 : RIALTO_SERVER_LOG_WARN("Failed to clean the decrypt context for the key session");
255 : }
256 6 : status = MediaKeyErrorStatus::OK;
257 : }
258 : else
259 : {
260 7 : status = m_ocdmSession->close();
261 7 : if (MediaKeyErrorStatus::OK != status)
262 : {
263 1 : RIALTO_SERVER_LOG_ERROR("Failed to Close the key session");
264 : }
265 : }
266 13 : m_isSessionClosed = (MediaKeyErrorStatus::OK == status);
267 :
268 13 : if ((checkForOcdmErrors("closeKeySession")) && (MediaKeyErrorStatus::OK == status))
269 : {
270 1 : status = MediaKeyErrorStatus::FAIL;
271 : }
272 :
273 13 : return status;
274 : }
275 :
276 3 : MediaKeyErrorStatus MediaKeySession::removeKeySession()
277 : {
278 3 : initOcdmErrorChecking();
279 :
280 3 : MediaKeyErrorStatus status = m_ocdmSession->remove();
281 3 : if (MediaKeyErrorStatus::OK != status)
282 : {
283 1 : RIALTO_SERVER_LOG_ERROR("Failed to remove the key session");
284 : }
285 :
286 3 : if ((checkForOcdmErrors("removeKeySession")) && (MediaKeyErrorStatus::OK == status))
287 : {
288 1 : status = MediaKeyErrorStatus::FAIL;
289 : }
290 :
291 3 : return status;
292 : }
293 :
294 3 : MediaKeyErrorStatus MediaKeySession::getCdmKeySessionId(std::string &cdmKeySessionId)
295 : {
296 3 : initOcdmErrorChecking();
297 :
298 3 : MediaKeyErrorStatus status = m_ocdmSession->getCdmKeySessionId(cdmKeySessionId);
299 3 : if (MediaKeyErrorStatus::OK != status)
300 : {
301 1 : RIALTO_SERVER_LOG_ERROR("Failed to get cdm key session id");
302 : }
303 :
304 3 : if ((checkForOcdmErrors("getCdmKeySessionId")) && (MediaKeyErrorStatus::OK == status))
305 : {
306 1 : status = MediaKeyErrorStatus::FAIL;
307 : }
308 :
309 3 : return status;
310 : }
311 :
312 2 : bool MediaKeySession::containsKey(const std::vector<uint8_t> &keyId)
313 : {
314 2 : uint32_t result = m_ocdmSession->hasKeyId(keyId.data(), keyId.size());
315 :
316 2 : return static_cast<bool>(result);
317 : }
318 :
319 3 : MediaKeyErrorStatus MediaKeySession::setDrmHeader(const std::vector<uint8_t> &requestData)
320 : {
321 3 : initOcdmErrorChecking();
322 :
323 3 : MediaKeyErrorStatus status = m_ocdmSession->setDrmHeader(requestData.data(), requestData.size());
324 3 : if (MediaKeyErrorStatus::OK != status)
325 : {
326 1 : RIALTO_SERVER_LOG_ERROR("Failed to set drm header");
327 : }
328 :
329 3 : if ((checkForOcdmErrors("setDrmHeader")) && (MediaKeyErrorStatus::OK == status))
330 : {
331 1 : status = MediaKeyErrorStatus::FAIL;
332 : }
333 :
334 3 : return status;
335 : }
336 :
337 3 : MediaKeyErrorStatus MediaKeySession::getLastDrmError(uint32_t &errorCode)
338 : {
339 3 : initOcdmErrorChecking();
340 :
341 3 : MediaKeyErrorStatus status = m_ocdmSession->getLastDrmError(errorCode);
342 3 : if (MediaKeyErrorStatus::OK != status)
343 : {
344 1 : RIALTO_SERVER_LOG_ERROR("Failed to get last drm error");
345 : }
346 :
347 3 : if ((checkForOcdmErrors("getLastDrmError")) && (MediaKeyErrorStatus::OK == status))
348 : {
349 1 : status = MediaKeyErrorStatus::FAIL;
350 : }
351 :
352 3 : return status;
353 : }
354 :
355 7 : MediaKeyErrorStatus MediaKeySession::selectKeyId(const std::vector<uint8_t> &keyId)
356 : {
357 7 : if (m_selectedKeyId == keyId)
358 : {
359 1 : return MediaKeyErrorStatus::OK;
360 : }
361 :
362 6 : initOcdmErrorChecking();
363 :
364 6 : MediaKeyErrorStatus status = m_ocdmSession->selectKeyId(keyId.size(), keyId.data());
365 6 : if (MediaKeyErrorStatus::OK == status)
366 : {
367 4 : RIALTO_SERVER_LOG_INFO("New keyId selected successfully");
368 4 : m_selectedKeyId = keyId;
369 : }
370 :
371 6 : if ((checkForOcdmErrors("selectKeyId")) && (MediaKeyErrorStatus::OK == status))
372 : {
373 1 : status = MediaKeyErrorStatus::FAIL;
374 : }
375 :
376 6 : return status;
377 : }
378 :
379 28 : bool MediaKeySession::isNetflixPlayreadyKeySystem() const
380 : {
381 28 : return m_kKeySystem.find("netflix") != std::string::npos;
382 : }
383 :
384 3 : void MediaKeySession::onProcessChallenge(const char url[], const uint8_t challenge[], const uint16_t challengeLength)
385 : {
386 3 : std::string urlStr = url;
387 6 : std::vector<unsigned char> challengeVec = std::vector<unsigned char>{challenge, challenge + challengeLength};
388 3 : auto task = [&, urlStr, challengeVec]()
389 : {
390 3 : std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
391 3 : if (client)
392 : {
393 3 : if (m_licenseRequested)
394 : {
395 2 : client->onLicenseRequest(m_kKeySessionId, challengeVec, urlStr);
396 2 : m_licenseRequested = false;
397 : }
398 : else
399 : {
400 1 : client->onLicenseRenewal(m_kKeySessionId, challengeVec);
401 : }
402 : }
403 6 : };
404 3 : m_mainThread->enqueueTask(m_mainThreadClientId, task);
405 : }
406 :
407 3 : void MediaKeySession::onKeyUpdated(const uint8_t keyId[], const uint8_t keyIdLength)
408 : {
409 6 : std::vector<unsigned char> keyIdVec = std::vector<unsigned char>{keyId, keyId + keyIdLength};
410 3 : auto task = [&, keyIdVec]()
411 : {
412 3 : std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
413 3 : if (client)
414 : {
415 3 : KeyStatus status = m_ocdmSession->getStatus(&keyIdVec[0], keyIdVec.size());
416 3 : m_updatedKeyStatuses.push_back(std::make_pair(keyIdVec, status));
417 : }
418 6 : };
419 3 : m_mainThread->enqueueTask(m_mainThreadClientId, task);
420 : }
421 :
422 1 : void MediaKeySession::onAllKeysUpdated()
423 : {
424 1 : auto task = [&]()
425 : {
426 1 : std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
427 1 : if (client)
428 : {
429 1 : client->onKeyStatusesChanged(m_kKeySessionId, m_updatedKeyStatuses);
430 1 : m_updatedKeyStatuses.clear();
431 : }
432 1 : };
433 1 : m_mainThread->enqueueTask(m_mainThreadClientId, task);
434 : }
435 :
436 10 : void MediaKeySession::onError(const char message[])
437 : {
438 10 : RIALTO_SERVER_LOG_ERROR("Ocdm returned error: %s", message);
439 :
440 10 : std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
441 10 : if (!m_ongoingOcdmOperation)
442 : {
443 0 : RIALTO_SERVER_LOG_WARN("Received an asycronous OCDM error, ignoring");
444 : }
445 : else
446 : {
447 10 : m_ocdmError = true;
448 : }
449 : }
450 :
451 50 : void MediaKeySession::initOcdmErrorChecking()
452 : {
453 50 : std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
454 50 : m_ongoingOcdmOperation = true;
455 50 : m_ocdmError = false;
456 : }
457 :
458 50 : bool MediaKeySession::checkForOcdmErrors(const char *operationStr)
459 : {
460 50 : bool error = false;
461 :
462 50 : std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
463 50 : if (m_ocdmError)
464 : {
465 10 : RIALTO_SERVER_LOG_ERROR("MediaKeySession received an onError callback, operation '%s' failed", operationStr);
466 10 : error = true;
467 : }
468 50 : m_ongoingOcdmOperation = false;
469 :
470 50 : return error;
471 : }
472 :
473 : }; // namespace firebolt::rialto::server
|