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 51 : RIALTO_SERVER_LOG_MIL("New OCDM session created");
85 67 : }
86 :
87 153 : MediaKeySession::~MediaKeySession()
88 : {
89 51 : RIALTO_SERVER_LOG_DEBUG("entry:");
90 :
91 51 : if (m_isSessionConstructed)
92 : {
93 7 : if (!m_isSessionClosed)
94 : {
95 7 : if (MediaKeyErrorStatus::OK != closeKeySession())
96 : {
97 0 : RIALTO_SERVER_LOG_ERROR("Failed to close the key session");
98 : }
99 : }
100 7 : if (MediaKeyErrorStatus::OK != m_ocdmSession->destructSession())
101 : {
102 0 : RIALTO_SERVER_LOG_ERROR("Failed to destruct the key session");
103 : }
104 : }
105 :
106 51 : m_mainThread->unregisterClient(m_mainThreadClientId);
107 102 : }
108 :
109 9 : MediaKeyErrorStatus MediaKeySession::generateRequest(InitDataType initDataType, const std::vector<uint8_t> &initData)
110 : {
111 9 : RIALTO_SERVER_LOG_DEBUG("entry:");
112 : // Set the request flag for the onLicenseRequest callback
113 9 : m_licenseRequested = true;
114 :
115 : // Only construct session if it hasnt previously been constructed
116 9 : if (!m_isSessionConstructed)
117 : {
118 8 : initOcdmErrorChecking();
119 :
120 : MediaKeyErrorStatus status =
121 8 : m_ocdmSession->constructSession(m_kSessionType, initDataType, &initData[0], initData.size());
122 8 : if (MediaKeyErrorStatus::OK != status)
123 : {
124 1 : RIALTO_SERVER_LOG_ERROR("Failed to construct the key session");
125 1 : m_licenseRequested = false;
126 : }
127 : else
128 : {
129 7 : m_isSessionConstructed = true;
130 7 : if (isNetflixPlayreadyKeySystem())
131 : {
132 : // Ocdm-playready does not notify onProcessChallenge when complete.
133 : // Fetch the challenge manually.
134 3 : getChallenge();
135 : }
136 : }
137 :
138 8 : if ((checkForOcdmErrors("generateRequest")) && (MediaKeyErrorStatus::OK == status))
139 : {
140 1 : status = MediaKeyErrorStatus::FAIL;
141 : }
142 :
143 8 : return status;
144 : }
145 :
146 1 : return MediaKeyErrorStatus::OK;
147 : }
148 :
149 3 : void MediaKeySession::getChallenge()
150 : {
151 3 : RIALTO_SERVER_LOG_DEBUG("entry:");
152 3 : auto task = [&]()
153 : {
154 3 : uint32_t challengeSize = 0;
155 3 : MediaKeyErrorStatus status = m_ocdmSession->getChallengeData(m_kIsLDL, nullptr, &challengeSize);
156 3 : if (challengeSize == 0)
157 : {
158 1 : RIALTO_SERVER_LOG_ERROR("Failed to get the challenge data size, no onLicenseRequest will be generated");
159 2 : return;
160 : }
161 2 : std::vector<uint8_t> challenge(challengeSize, 0x00);
162 2 : status = m_ocdmSession->getChallengeData(m_kIsLDL, &challenge[0], &challengeSize);
163 2 : if (MediaKeyErrorStatus::OK != status)
164 : {
165 1 : RIALTO_SERVER_LOG_ERROR("Failed to get the challenge data, no onLicenseRequest will be generated");
166 1 : return;
167 : }
168 :
169 1 : std::string url;
170 1 : onProcessChallenge(url.c_str(), &challenge[0], challengeSize);
171 2 : };
172 3 : m_mainThread->enqueueTask(m_mainThreadClientId, task);
173 : }
174 :
175 3 : MediaKeyErrorStatus MediaKeySession::loadSession()
176 : {
177 3 : initOcdmErrorChecking();
178 :
179 3 : MediaKeyErrorStatus status = m_ocdmSession->load();
180 3 : if (MediaKeyErrorStatus::OK != status)
181 : {
182 1 : RIALTO_SERVER_LOG_ERROR("Failed to load the key session");
183 : }
184 :
185 3 : if ((checkForOcdmErrors("loadSession")) && (MediaKeyErrorStatus::OK == status))
186 : {
187 1 : status = MediaKeyErrorStatus::FAIL;
188 : }
189 :
190 3 : return status;
191 : }
192 :
193 5 : MediaKeyErrorStatus MediaKeySession::updateSession(const std::vector<uint8_t> &responseData)
194 : {
195 5 : initOcdmErrorChecking();
196 :
197 : MediaKeyErrorStatus status;
198 5 : if (isNetflixPlayreadyKeySystem())
199 : {
200 2 : status = m_ocdmSession->storeLicenseData(&responseData[0], responseData.size());
201 2 : if (MediaKeyErrorStatus::OK != status)
202 : {
203 1 : RIALTO_SERVER_LOG_ERROR("Failed to store the license data for the key session");
204 : }
205 : }
206 : else
207 : {
208 3 : status = m_ocdmSession->update(&responseData[0], responseData.size());
209 3 : if (MediaKeyErrorStatus::OK != status)
210 : {
211 1 : RIALTO_SERVER_LOG_ERROR("Failed to update the key session");
212 : }
213 : }
214 :
215 5 : if ((checkForOcdmErrors("updateSession")) && (MediaKeyErrorStatus::OK == status))
216 : {
217 1 : status = MediaKeyErrorStatus::FAIL;
218 : }
219 :
220 5 : return status;
221 : }
222 :
223 3 : MediaKeyErrorStatus MediaKeySession::decrypt(GstBuffer *encrypted, GstCaps *caps)
224 : {
225 3 : initOcdmErrorChecking();
226 :
227 3 : MediaKeyErrorStatus status = m_ocdmSession->decryptBuffer(encrypted, caps);
228 3 : if (MediaKeyErrorStatus::OK != status)
229 : {
230 1 : RIALTO_SERVER_LOG_ERROR("Failed to decrypt buffer");
231 : }
232 :
233 3 : if ((checkForOcdmErrors("decrypt")) && (MediaKeyErrorStatus::OK == status))
234 : {
235 1 : status = MediaKeyErrorStatus::FAIL;
236 : }
237 :
238 3 : return status;
239 : }
240 :
241 13 : MediaKeyErrorStatus MediaKeySession::closeKeySession()
242 : {
243 13 : initOcdmErrorChecking();
244 :
245 : MediaKeyErrorStatus status;
246 13 : if (isNetflixPlayreadyKeySystem())
247 : {
248 6 : if (MediaKeyErrorStatus::OK != m_ocdmSession->cancelChallengeData())
249 : {
250 1 : RIALTO_SERVER_LOG_WARN("Failed to cancel the challenge data for the key session");
251 : }
252 :
253 6 : if (MediaKeyErrorStatus::OK != m_ocdmSession->cleanDecryptContext())
254 : {
255 1 : RIALTO_SERVER_LOG_WARN("Failed to clean the decrypt context for the key session");
256 : }
257 6 : status = MediaKeyErrorStatus::OK;
258 6 : RIALTO_SERVER_LOG_MIL("OCDM session closed");
259 : }
260 : else
261 : {
262 7 : status = m_ocdmSession->close();
263 7 : if (MediaKeyErrorStatus::OK != status)
264 : {
265 1 : RIALTO_SERVER_LOG_ERROR("Failed to Close the key session");
266 : }
267 : }
268 13 : m_isSessionClosed = (MediaKeyErrorStatus::OK == status);
269 :
270 13 : if ((checkForOcdmErrors("closeKeySession")) && (MediaKeyErrorStatus::OK == status))
271 : {
272 1 : status = MediaKeyErrorStatus::FAIL;
273 : }
274 :
275 13 : return status;
276 : }
277 :
278 3 : MediaKeyErrorStatus MediaKeySession::removeKeySession()
279 : {
280 3 : initOcdmErrorChecking();
281 :
282 3 : MediaKeyErrorStatus status = m_ocdmSession->remove();
283 3 : if (MediaKeyErrorStatus::OK != status)
284 : {
285 1 : RIALTO_SERVER_LOG_ERROR("Failed to remove the key session");
286 : }
287 :
288 3 : if ((checkForOcdmErrors("removeKeySession")) && (MediaKeyErrorStatus::OK == status))
289 : {
290 1 : status = MediaKeyErrorStatus::FAIL;
291 : }
292 :
293 3 : return status;
294 : }
295 :
296 3 : MediaKeyErrorStatus MediaKeySession::getCdmKeySessionId(std::string &cdmKeySessionId)
297 : {
298 3 : initOcdmErrorChecking();
299 :
300 3 : MediaKeyErrorStatus status = m_ocdmSession->getCdmKeySessionId(cdmKeySessionId);
301 3 : if (MediaKeyErrorStatus::OK != status)
302 : {
303 1 : RIALTO_SERVER_LOG_ERROR("Failed to get cdm key session id");
304 : }
305 :
306 3 : if ((checkForOcdmErrors("getCdmKeySessionId")) && (MediaKeyErrorStatus::OK == status))
307 : {
308 1 : status = MediaKeyErrorStatus::FAIL;
309 : }
310 :
311 3 : return status;
312 : }
313 :
314 2 : bool MediaKeySession::containsKey(const std::vector<uint8_t> &keyId)
315 : {
316 2 : uint32_t result = m_ocdmSession->hasKeyId(keyId.data(), keyId.size());
317 :
318 2 : return static_cast<bool>(result);
319 : }
320 :
321 3 : MediaKeyErrorStatus MediaKeySession::setDrmHeader(const std::vector<uint8_t> &requestData)
322 : {
323 3 : initOcdmErrorChecking();
324 :
325 3 : MediaKeyErrorStatus status = m_ocdmSession->setDrmHeader(requestData.data(), requestData.size());
326 3 : if (MediaKeyErrorStatus::OK != status)
327 : {
328 1 : RIALTO_SERVER_LOG_ERROR("Failed to set drm header");
329 : }
330 :
331 3 : if ((checkForOcdmErrors("setDrmHeader")) && (MediaKeyErrorStatus::OK == status))
332 : {
333 1 : status = MediaKeyErrorStatus::FAIL;
334 : }
335 :
336 3 : return status;
337 : }
338 :
339 3 : MediaKeyErrorStatus MediaKeySession::getLastDrmError(uint32_t &errorCode)
340 : {
341 3 : initOcdmErrorChecking();
342 :
343 3 : MediaKeyErrorStatus status = m_ocdmSession->getLastDrmError(errorCode);
344 3 : if (MediaKeyErrorStatus::OK != status)
345 : {
346 1 : RIALTO_SERVER_LOG_ERROR("Failed to get last drm error");
347 : }
348 :
349 3 : if ((checkForOcdmErrors("getLastDrmError")) && (MediaKeyErrorStatus::OK == status))
350 : {
351 1 : status = MediaKeyErrorStatus::FAIL;
352 : }
353 :
354 3 : return status;
355 : }
356 :
357 7 : MediaKeyErrorStatus MediaKeySession::selectKeyId(const std::vector<uint8_t> &keyId)
358 : {
359 7 : if (m_selectedKeyId == keyId)
360 : {
361 1 : return MediaKeyErrorStatus::OK;
362 : }
363 :
364 6 : initOcdmErrorChecking();
365 :
366 6 : MediaKeyErrorStatus status = m_ocdmSession->selectKeyId(keyId.size(), keyId.data());
367 6 : if (MediaKeyErrorStatus::OK == status)
368 : {
369 4 : RIALTO_SERVER_LOG_INFO("New keyId selected successfully");
370 4 : m_selectedKeyId = keyId;
371 : }
372 :
373 6 : if ((checkForOcdmErrors("selectKeyId")) && (MediaKeyErrorStatus::OK == status))
374 : {
375 1 : status = MediaKeyErrorStatus::FAIL;
376 : }
377 :
378 6 : return status;
379 : }
380 :
381 28 : bool MediaKeySession::isNetflixPlayreadyKeySystem() const
382 : {
383 28 : return m_kKeySystem.find("netflix") != std::string::npos;
384 : }
385 :
386 3 : void MediaKeySession::onProcessChallenge(const char url[], const uint8_t challenge[], const uint16_t challengeLength)
387 : {
388 3 : std::string urlStr = url;
389 6 : std::vector<unsigned char> challengeVec = std::vector<unsigned char>{challenge, challenge + challengeLength};
390 3 : auto task = [&, urlStr, challengeVec]()
391 : {
392 3 : std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
393 3 : if (client)
394 : {
395 3 : if (m_licenseRequested)
396 : {
397 2 : client->onLicenseRequest(m_kKeySessionId, challengeVec, urlStr);
398 2 : m_licenseRequested = false;
399 : }
400 : else
401 : {
402 1 : client->onLicenseRenewal(m_kKeySessionId, challengeVec);
403 : }
404 : }
405 6 : };
406 3 : m_mainThread->enqueueTask(m_mainThreadClientId, task);
407 : }
408 :
409 3 : void MediaKeySession::onKeyUpdated(const uint8_t keyId[], const uint8_t keyIdLength)
410 : {
411 6 : std::vector<unsigned char> keyIdVec = std::vector<unsigned char>{keyId, keyId + keyIdLength};
412 3 : auto task = [&, keyIdVec]()
413 : {
414 3 : std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
415 3 : if (client)
416 : {
417 3 : KeyStatus status = m_ocdmSession->getStatus(&keyIdVec[0], keyIdVec.size());
418 3 : m_updatedKeyStatuses.push_back(std::make_pair(keyIdVec, status));
419 : }
420 6 : };
421 3 : m_mainThread->enqueueTask(m_mainThreadClientId, task);
422 : }
423 :
424 1 : void MediaKeySession::onAllKeysUpdated()
425 : {
426 1 : auto task = [&]()
427 : {
428 1 : std::shared_ptr<IMediaKeysClient> client = m_mediaKeysClient.lock();
429 1 : if (client)
430 : {
431 1 : client->onKeyStatusesChanged(m_kKeySessionId, m_updatedKeyStatuses);
432 1 : m_updatedKeyStatuses.clear();
433 : }
434 1 : };
435 1 : m_mainThread->enqueueTask(m_mainThreadClientId, task);
436 : }
437 :
438 10 : void MediaKeySession::onError(const char message[])
439 : {
440 10 : RIALTO_SERVER_LOG_ERROR("Ocdm returned error: %s", message);
441 :
442 10 : std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
443 10 : if (!m_ongoingOcdmOperation)
444 : {
445 0 : RIALTO_SERVER_LOG_WARN("Received an asycronous OCDM error, ignoring");
446 : }
447 : else
448 : {
449 10 : m_ocdmError = true;
450 : }
451 : }
452 :
453 50 : void MediaKeySession::initOcdmErrorChecking()
454 : {
455 50 : std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
456 50 : m_ongoingOcdmOperation = true;
457 50 : m_ocdmError = false;
458 : }
459 :
460 50 : bool MediaKeySession::checkForOcdmErrors(const char *operationStr)
461 : {
462 50 : bool error = false;
463 :
464 50 : std::lock_guard<std::mutex> lock(m_ocdmErrorMutex);
465 50 : if (m_ocdmError)
466 : {
467 10 : RIALTO_SERVER_LOG_ERROR("MediaKeySession received an onError callback, operation '%s' failed", operationStr);
468 10 : error = true;
469 : }
470 50 : m_ongoingOcdmOperation = false;
471 :
472 50 : return error;
473 : }
474 :
475 : }; // namespace firebolt::rialto::server
|