tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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