WMFClearKeyCDM.cpp (13900B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "WMFClearKeyCDM.h" 6 7 #include <Mferror.h> 8 #include <mfapi.h> 9 #include <oleauto.h> 10 #include <windows.h> 11 #include <windows.media.h> 12 13 #include "WMFClearKeyTrustedInput.h" 14 #include "WMFClearKeySession.h" 15 #include "WMFDecryptedBlock.h" 16 #include "WMFPMPServer.h" 17 18 using Microsoft::WRL::ComPtr; 19 using Microsoft::WRL::MakeAndInitialize; 20 21 static HRESULT AddPropertyToSet( 22 ABI::Windows::Foundation::Collections::IPropertySet* aPropertySet, 23 LPCWSTR aName, IInspectable* aInspectable) { 24 boolean replaced = false; 25 ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>> 26 map; 27 RETURN_IF_FAILED(aPropertySet->QueryInterface(IID_PPV_ARGS(&map))); 28 RETURN_IF_FAILED( 29 map->Insert(Microsoft::WRL::Wrappers::HStringReference(aName).Get(), 30 aInspectable, &replaced)); 31 return S_OK; 32 } 33 34 static HRESULT AddStringToPropertySet( 35 ABI::Windows::Foundation::Collections::IPropertySet* aPropertySet, 36 LPCWSTR aName, LPCWSTR aString) { 37 ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue; 38 ComPtr<ABI::Windows::Foundation::IPropertyValueStatics> propertyValueStatics; 39 RETURN_IF_FAILED(ABI::Windows::Foundation::GetActivationFactory( 40 Microsoft::WRL::Wrappers::HStringReference( 41 RuntimeClass_Windows_Foundation_PropertyValue) 42 .Get(), 43 &propertyValueStatics)); 44 RETURN_IF_FAILED(propertyValueStatics->CreateString( 45 Microsoft::WRL::Wrappers::HStringReference(aString).Get(), 46 &propertyValue)); 47 RETURN_IF_FAILED(AddPropertyToSet(aPropertySet, aName, propertyValue.Get())); 48 return S_OK; 49 } 50 51 static HRESULT AddBoolToPropertySet( 52 ABI::Windows::Foundation::Collections::IPropertySet* aPropertySet, 53 LPCWSTR aName, BOOL aValue) { 54 ComPtr<ABI::Windows::Foundation::IPropertyValue> propertyValue; 55 ComPtr<ABI::Windows::Foundation::IPropertyValueStatics> propertyValueStatics; 56 RETURN_IF_FAILED(ABI::Windows::Foundation::GetActivationFactory( 57 Microsoft::WRL::Wrappers::HStringReference( 58 RuntimeClass_Windows_Foundation_PropertyValue) 59 .Get(), 60 &propertyValueStatics)); 61 RETURN_IF_FAILED( 62 propertyValueStatics->CreateBoolean(!!aValue, &propertyValue)); 63 RETURN_IF_FAILED(AddPropertyToSet(aPropertySet, aName, propertyValue.Get())); 64 return S_OK; 65 } 66 67 MF_MEDIAKEYSESSION_MESSAGETYPE ToMFMessageType(cdm::MessageType aMessageType) { 68 switch (aMessageType) { 69 case cdm::MessageType::kLicenseRequest: 70 return MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_REQUEST; 71 case cdm::MessageType::kLicenseRenewal: 72 return MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RENEWAL; 73 case cdm::MessageType::kLicenseRelease: 74 return MF_MEDIAKEYSESSION_MESSAGETYPE_LICENSE_RELEASE; 75 case cdm::MessageType::kIndividualizationRequest: 76 return MF_MEDIAKEYSESSION_MESSAGETYPE_INDIVIDUALIZATION_REQUEST; 77 } 78 } 79 80 namespace mozilla { 81 82 HRESULT WMFClearKeyCDM::RuntimeClassInitialize(IPropertyStore* aProperties) { 83 ENTRY_LOG(); 84 // A workaround in order to create an in-process PMP server regardless of the 85 // system's HWDRM capability. As accoriding to Microsoft, only PlayReady is 86 // supported for the in-process PMP so we pretend ourselves as a PlayReady 87 // CDM for the PMP server. 88 ComPtr<ABI::Windows::Foundation::Collections::IPropertySet> propertyPmp; 89 RETURN_IF_FAILED(Windows::Foundation::ActivateInstance( 90 Microsoft::WRL::Wrappers::HStringReference( 91 RuntimeClass_Windows_Foundation_Collections_PropertySet) 92 .Get(), 93 &propertyPmp)); 94 RETURN_IF_FAILED(AddStringToPropertySet( 95 propertyPmp.Get(), L"Windows.Media.Protection.MediaProtectionSystemId", 96 PLAYREADY_GUID_MEDIA_PROTECTION_SYSTEM_ID_STRING)); 97 RETURN_IF_FAILED(AddBoolToPropertySet( 98 propertyPmp.Get(), L"Windows.Media.Protection.UseHardwareProtectionLayer", 99 TRUE)); 100 RETURN_IF_FAILED((MakeAndInitialize< 101 WMFPMPServer, 102 ABI::Windows::Media::Protection::IMediaProtectionPMPServer>( 103 &mPMPServer, propertyPmp.Get()))); 104 105 mSessionManager = new SessionManagerWrapper(this); 106 return S_OK; 107 } 108 109 WMFClearKeyCDM::~WMFClearKeyCDM() { ENTRY_LOG(); } 110 111 STDMETHODIMP WMFClearKeyCDM::SetContentEnabler( 112 IMFContentEnabler* aContentEnabler, IMFAsyncResult* aResult) { 113 ENTRY_LOG(); 114 if (!aContentEnabler || !aResult) { 115 return E_INVALIDARG; 116 } 117 // Invoke the callback immediately but will determine whether the keyid exists 118 // or not in the decryptor's ProcessOutput(). 119 RETURN_IF_FAILED(MFInvokeCallback(aResult)); 120 return S_OK; 121 } 122 123 STDMETHODIMP WMFClearKeyCDM::SetPMPHostApp(IMFPMPHostApp* aPmpHostApp) { 124 ENTRY_LOG(); 125 // Simply return S_OK and ignore IMFPMPHostApp, which is only used for the 126 // out-of-process PMP. 127 return S_OK; 128 } 129 130 STDMETHODIMP WMFClearKeyCDM::CreateSession( 131 MF_MEDIAKEYSESSION_TYPE aSessionType, 132 IMFContentDecryptionModuleSessionCallbacks* aCallbacks, 133 IMFContentDecryptionModuleSession** aSession) { 134 ENTRY_LOG(); 135 RETURN_IF_FAILED( 136 (MakeAndInitialize<WMFClearKeySession, IMFContentDecryptionModuleSession>( 137 aSession, aSessionType, aCallbacks, mSessionManager))); 138 return S_OK; 139 } 140 141 STDMETHODIMP WMFClearKeyCDM::CreateTrustedInput( 142 const BYTE* aContentInitData, DWORD aContentInitDataSize, 143 IMFTrustedInput** aTrustedInput) { 144 ENTRY_LOG(); 145 ComPtr<IMFTrustedInput> trustedInput; 146 RETURN_IF_FAILED((MakeAndInitialize<WMFClearKeyTrustedInput, IMFTrustedInput>( 147 &trustedInput, mSessionManager))); 148 *aTrustedInput = trustedInput.Detach(); 149 return S_OK; 150 } 151 152 STDMETHODIMP WMFClearKeyCDM::GetProtectionSystemIds(GUID** aSystemIds, 153 DWORD* aCount) { 154 ENTRY_LOG(); 155 GUID* systemId = static_cast<GUID*>(CoTaskMemAlloc(sizeof(GUID))); 156 if (!systemId) { 157 return E_OUTOFMEMORY; 158 } 159 *systemId = CLEARKEY_GUID_CLEARKEY_PROTECTION_SYSTEM_ID; 160 *aSystemIds = systemId; 161 *aCount = 1; 162 return S_OK; 163 } 164 165 STDMETHODIMP WMFClearKeyCDM::GetService(REFGUID aGuidService, REFIID aRiid, 166 LPVOID* aPpvObject) { 167 ENTRY_LOG(); 168 if (MF_CONTENTDECRYPTIONMODULE_SERVICE != aGuidService) { 169 ENTRY_LOG_ARGS("unsupported guid!"); 170 return MF_E_UNSUPPORTED_SERVICE; 171 } 172 if (!mPMPServer) { 173 ENTRY_LOG_ARGS("no PMP server!"); 174 return MF_INVALID_STATE_ERR; 175 } 176 if (aRiid == ABI::Windows::Media::Protection::IID_IMediaProtectionPMPServer) { 177 RETURN_IF_FAILED(mPMPServer.CopyTo(aRiid, aPpvObject)); 178 } else { 179 ComPtr<IMFGetService> getService; 180 RETURN_IF_FAILED(mPMPServer.As(&getService)); 181 RETURN_IF_FAILED(getService->GetService(MF_PMP_SERVICE, aRiid, aPpvObject)); 182 } 183 return S_OK; 184 } 185 186 STDMETHODIMP WMFClearKeyCDM::GetSuspendNotify(IMFCdmSuspendNotify** aNotify) { 187 NOT_IMPLEMENTED(); 188 return E_NOTIMPL; 189 } 190 191 STDMETHODIMP WMFClearKeyCDM::SetServerCertificate(const BYTE* aCertificate, 192 DWORD aCertificateSize) { 193 NOT_IMPLEMENTED(); 194 return E_NOTIMPL; 195 } 196 197 STDMETHODIMP WMFClearKeyCDM::Shutdown() { 198 ENTRY_LOG(); 199 mSessionManager->Shutdown(); 200 return S_OK; 201 } 202 203 STDMETHODIMP WMFClearKeyCDM::GetShutdownStatus(MFSHUTDOWN_STATUS* aStatus) { 204 ENTRY_LOG(); 205 // https://learn.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfshutdown-getshutdownstatus#return-value 206 if (mSessionManager->IsShutdown()) { 207 return MF_E_INVALIDREQUEST; 208 } 209 return S_OK; 210 } 211 212 // SessionManagerWrapper 213 214 SessionManagerWrapper::SessionManagerWrapper(WMFClearKeyCDM* aCDM) 215 : mOwnerCDM(aCDM), mSessionManager(new ClearKeySessionManager(this)) { 216 // For testing, we don't care about these. 217 mSessionManager->Init(false /* aDistinctiveIdentifierAllowed */, 218 false /* aPersistentStateAllowed*/); 219 } 220 221 SessionManagerWrapper::~SessionManagerWrapper() { ENTRY_LOG(); } 222 223 // Callback methods 224 void SessionManagerWrapper::OnResolveNewSessionPromise( 225 uint32_t aPromiseId, const char* aSessionId, uint32_t aSessionIdSize) { 226 if (auto rv = mActiveSyncResultChecker.find(aPromiseId); 227 rv != mActiveSyncResultChecker.end()) { 228 LOG("Generated request (promise-id=%u, sessionId=%s)", aPromiseId, 229 aSessionId); 230 rv->second->SetResultConstChar(aSessionId); 231 std::string sessionId{aSessionId}; 232 MOZ_ASSERT(mSessions.find(sessionId) == mSessions.end()); 233 mSessions[sessionId] = rv->second->GetKeySession(); 234 } 235 } 236 237 void SessionManagerWrapper::OnResolvePromise(uint32_t aPromiseId) { 238 if (auto rv = mActiveSyncResultChecker.find(aPromiseId); 239 rv != mActiveSyncResultChecker.end()) { 240 LOG("Resolved promise (promise-id=%u)", aPromiseId); 241 rv->second->SetResultBool(true); 242 } 243 } 244 245 void SessionManagerWrapper::OnRejectPromise(uint32_t aPromiseId, 246 cdm::Exception aException, 247 uint32_t aSystemCode, 248 const char* aErrorMessage, 249 uint32_t aErrorMessageSize) { 250 if (auto rv = mActiveSyncResultChecker.find(aPromiseId); 251 rv != mActiveSyncResultChecker.end()) { 252 LOG("Rejected promise (promise-id=%u)", aPromiseId); 253 rv->second->SetResultBool(false); 254 } 255 } 256 257 HRESULT SessionManagerWrapper::GenerateRequest(cdm::InitDataType aInitDataType, 258 const BYTE* aInitData, 259 DWORD aInitDataSize, 260 cdm::SessionType aSessionType, 261 WMFClearKeySession* aSession, 262 std::string& aSessionIdOut) { 263 ENTRY_LOG(); 264 MOZ_DIAGNOSTIC_ASSERT(aSessionType == cdm::SessionType::kTemporary); 265 SyncResultChecker checker(*this, aSession); 266 mSessionManager->CreateSession(checker.GetPromiseId(), aInitDataType, 267 aInitData, aInitDataSize, aSessionType); 268 auto* rv = std::get_if<const char*>(&checker.GetResult()); 269 if (!rv) { 270 LOG("Failed to generate request, no session Id!"); 271 return E_FAIL; 272 } 273 aSessionIdOut = std::string(*rv); 274 return S_OK; 275 } 276 277 HRESULT SessionManagerWrapper::UpdateSession(const std::string& aSessionId, 278 const BYTE* aResponse, 279 DWORD aResponseSize) { 280 ENTRY_LOG(); 281 SyncResultChecker checker(*this); 282 mSessionManager->UpdateSession(checker.GetPromiseId(), aSessionId.c_str(), 283 aSessionId.size(), aResponse, aResponseSize); 284 auto* rv = std::get_if<bool>(&checker.GetResult()); 285 return rv && *rv ? S_OK : E_FAIL; 286 } 287 288 HRESULT SessionManagerWrapper::CloseSession(const std::string& aSessionId) { 289 ENTRY_LOG(); 290 SyncResultChecker checker(*this); 291 mSessionManager->CloseSession(checker.GetPromiseId(), aSessionId.c_str(), 292 aSessionId.size()); 293 auto* rv = std::get_if<bool>(&checker.GetResult()); 294 return rv && *rv ? S_OK : E_FAIL; 295 } 296 297 HRESULT SessionManagerWrapper::RemoveSession(const std::string& aSessionId) { 298 ENTRY_LOG(); 299 SyncResultChecker checker(*this); 300 MOZ_ASSERT(mSessions.find(aSessionId) != mSessions.end()); 301 mSessions.erase(aSessionId); 302 mSessionManager->RemoveSession(checker.GetPromiseId(), aSessionId.c_str(), 303 aSessionId.size()); 304 auto* rv = std::get_if<bool>(&checker.GetResult()); 305 return rv && *rv ? S_OK : E_FAIL; 306 } 307 308 HRESULT SessionManagerWrapper::Decrypt(const cdm::InputBuffer_2& aBuffer, 309 cdm::DecryptedBlock* aDecryptedBlock) { 310 ENTRY_LOG(); 311 // From MF thread pool. 312 std::lock_guard<std::mutex> lock(mMutex); 313 if (mIsShutdown) { 314 return MF_E_SHUTDOWN; 315 } 316 auto rv = mSessionManager->Decrypt(aBuffer, aDecryptedBlock); 317 return rv == cdm::kSuccess ? S_OK : E_FAIL; 318 } 319 320 void SessionManagerWrapper::OnSessionMessage(const char* aSessionId, 321 uint32_t aSessionIdSize, 322 cdm::MessageType aMessageType, 323 const char* aMessage, 324 uint32_t aMessageSize) { 325 ENTRY_LOG_ARGS("sessionId=%s, sz=%u", aSessionId, aSessionIdSize); 326 std::string sessionId(aSessionId); 327 MOZ_ASSERT(mSessions.find(sessionId) != mSessions.end()); 328 mSessions[sessionId]->OnKeyMessage(ToMFMessageType(aMessageType), 329 reinterpret_cast<const BYTE*>(aMessage), 330 aMessageSize); 331 } 332 333 void SessionManagerWrapper::OnSessionKeysChange( 334 const char* aSessionId, uint32_t aSessionIdSize, 335 bool aHasAdditionalUsableKey, const cdm::KeyInformation* aKeysInfo, 336 uint32_t aKeysInfoCount) { 337 ENTRY_LOG_ARGS("sessionId=%s, sz=%u", aSessionId, aSessionIdSize); 338 std::string sessionId(aSessionId); 339 MOZ_ASSERT(mSessions.find(sessionId) != mSessions.end()); 340 mSessions[sessionId]->OnKeyStatusChanged(aKeysInfo, aKeysInfoCount); 341 } 342 343 // `ClearKeySessionManager::Decrypt` will call this method to allocate buffer 344 // for decryted data. 345 cdm::Buffer* SessionManagerWrapper::Allocate(uint32_t aCapacity) { 346 ENTRY_LOG_ARGS("capacity=%u", aCapacity); 347 return new WMFDecryptedBuffer(aCapacity); 348 } 349 350 void SessionManagerWrapper::Shutdown() { 351 ENTRY_LOG(); 352 std::lock_guard<std::mutex> lock(mMutex); 353 if (mIsShutdown) { 354 return; 355 } 356 mOwnerCDM = nullptr; 357 for (const auto& session : mSessions) { 358 session.second->Shutdown(); 359 } 360 mSessions.clear(); 361 mSessionManager = nullptr; 362 mIsShutdown = true; 363 } 364 365 bool SessionManagerWrapper::IsShutdown() { 366 std::lock_guard<std::mutex> lock(mMutex); 367 return mIsShutdown; 368 } 369 370 } // namespace mozilla