tor-browser

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

KeySystemConfig.cpp (14523B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "KeySystemConfig.h"
      8 
      9 #include "EMEUtils.h"
     10 #include "GMPUtils.h"
     11 #include "KeySystemNames.h"
     12 #include "mozilla/StaticPrefs_media.h"
     13 #include "mozilla/dom/Promise.h"
     14 #include "nsPrintfCString.h"
     15 
     16 #ifdef XP_WIN
     17 #  include "PDMFactory.h"
     18 #  include "WMFDecoderModule.h"
     19 #endif
     20 #ifdef MOZ_WIDGET_ANDROID
     21 #  include "AndroidDecoderModule.h"
     22 #  include "mozilla/java/MediaDrmProxyWrappers.h"
     23 #  include "nsMimeTypes.h"
     24 #endif
     25 
     26 #ifdef MOZ_WMF_CDM
     27 #  include "mediafoundation/WMFCDMImpl.h"
     28 #endif
     29 
     30 namespace mozilla {
     31 
     32 /* static */
     33 bool KeySystemConfig::Supports(const nsAString& aKeySystem) {
     34 #ifdef MOZ_WIDGET_ANDROID
     35  // Check if we can use MediaDrm for this keysystem.
     36  if (mozilla::java::MediaDrmProxy::IsSchemeSupported(
     37          NS_ConvertUTF16toUTF8(aKeySystem))) {
     38    return true;
     39  }
     40  // Check if we can use our bundled Clearkey plugin.
     41  if (IsClearkeyKeySystem(aKeySystem)) {
     42    return HaveGMPFor(nsCString(CHROMIUM_CDM_API),
     43                      {NS_ConvertUTF16toUTF8(aKeySystem)});
     44  }
     45 #else
     46 #  ifdef MOZ_WMF_CDM
     47  // Test only, pretend we have already installed CDMs.
     48  if (StaticPrefs::media_eme_wmf_use_mock_cdm_for_external_cdms()) {
     49    return true;
     50  }
     51 #  endif
     52  // Check if Widevine L3 or Clearkey has been downloaded via GMP downloader.
     53  if (IsWidevineKeySystem(aKeySystem) || IsClearkeyKeySystem(aKeySystem)) {
     54    return HaveGMPFor(nsCString(CHROMIUM_CDM_API),
     55                      {NS_ConvertUTF16toUTF8(aKeySystem)});
     56  }
     57 #endif
     58 
     59 #if MOZ_WMF_CDM
     60  // Check if Widevine L1 has been downloaded via GMP downloader.
     61  if (IsWidevineExperimentKeySystemAndSupported(aKeySystem)) {
     62    return HaveGMPFor(nsCString(kWidevineExperimentAPIName),
     63                      {nsCString(kWidevineExperimentKeySystemName)});
     64  }
     65 
     66  // PlayReady and WMF-based ClearKey are always installed, we don't need to
     67  // download them.
     68  if (IsPlayReadyKeySystemAndSupported(aKeySystem) ||
     69      IsWMFClearKeySystemAndSupported(aKeySystem)) {
     70    return true;
     71  }
     72 #endif
     73 
     74  return false;
     75 }
     76 
     77 /* static */ void KeySystemConfig::CreateClearKeyKeySystemConfigs(
     78    const KeySystemConfigRequest& aRequest,
     79    nsTArray<KeySystemConfig>& aOutConfigs) {
     80  KeySystemConfig* config = aOutConfigs.AppendElement();
     81  config->mKeySystem = aRequest.mKeySystem;
     82  config->mInitDataTypes.AppendElement(u"cenc"_ns);
     83  config->mInitDataTypes.AppendElement(u"keyids"_ns);
     84  config->mInitDataTypes.AppendElement(u"webm"_ns);
     85  config->mPersistentState = Requirement::Optional;
     86  config->mDistinctiveIdentifier = Requirement::NotAllowed;
     87  config->mSessionTypes.AppendElement(SessionType::Temporary);
     88  if (StaticPrefs::media_clearkey_persistent_license_enabled()) {
     89    config->mSessionTypes.AppendElement(SessionType::PersistentLicense);
     90  }
     91 #if defined(XP_WIN)
     92  // Clearkey CDM uses WMF's H.264 decoder on Windows.
     93  auto pdmFactory = MakeRefPtr<PDMFactory>();
     94  if (!pdmFactory->SupportsMimeType("video/avc"_ns).isEmpty()) {
     95    config->mMP4.SetCanDecryptAndDecode(EME_CODEC_H264);
     96  } else {
     97    config->mMP4.SetCanDecrypt(EME_CODEC_H264);
     98  }
     99 #else
    100  config->mMP4.SetCanDecrypt(EME_CODEC_H264);
    101 #endif
    102  config->mMP4.SetCanDecrypt(EME_CODEC_AAC);
    103  config->mMP4.SetCanDecrypt(EME_CODEC_FLAC);
    104  config->mMP4.SetCanDecrypt(EME_CODEC_OPUS);
    105  config->mMP4.SetCanDecrypt(EME_CODEC_VP9);
    106 #ifdef MOZ_AV1
    107  config->mMP4.SetCanDecrypt(EME_CODEC_AV1);
    108 #endif
    109  config->mWebM.SetCanDecrypt(EME_CODEC_VORBIS);
    110  config->mWebM.SetCanDecrypt(EME_CODEC_OPUS);
    111  config->mWebM.SetCanDecrypt(EME_CODEC_VP8);
    112  config->mWebM.SetCanDecrypt(EME_CODEC_VP9);
    113 #ifdef MOZ_AV1
    114  config->mWebM.SetCanDecrypt(EME_CODEC_AV1);
    115 #endif
    116 
    117  if (StaticPrefs::media_clearkey_test_key_systems_enabled()) {
    118    // Add testing key systems. These offer the same capabilities as the
    119    // base clearkey system, so just clone clearkey and change the name.
    120    KeySystemConfig clearkeyWithProtectionQuery{*config};
    121    clearkeyWithProtectionQuery.mKeySystem.AssignLiteral(
    122        kClearKeyWithProtectionQueryKeySystemName);
    123    aOutConfigs.AppendElement(std::move(clearkeyWithProtectionQuery));
    124  }
    125 }
    126 
    127 /* static */ void KeySystemConfig::CreateWivineL3KeySystemConfigs(
    128    const KeySystemConfigRequest& aRequest,
    129    nsTArray<KeySystemConfig>& aOutConfigs) {
    130  KeySystemConfig* config = aOutConfigs.AppendElement();
    131  config->mKeySystem = aRequest.mKeySystem;
    132  config->mInitDataTypes.AppendElement(u"cenc"_ns);
    133  config->mInitDataTypes.AppendElement(u"keyids"_ns);
    134  config->mInitDataTypes.AppendElement(u"webm"_ns);
    135  config->mPersistentState = Requirement::Optional;
    136  config->mDistinctiveIdentifier = Requirement::NotAllowed;
    137  config->mSessionTypes.AppendElement(SessionType::Temporary);
    138 #ifdef MOZ_WIDGET_ANDROID
    139  config->mSessionTypes.AppendElement(SessionType::PersistentLicense);
    140 #endif
    141  config->mAudioRobustness.AppendElement(u"SW_SECURE_CRYPTO"_ns);
    142  config->mVideoRobustness.AppendElement(u"SW_SECURE_CRYPTO"_ns);
    143  config->mVideoRobustness.AppendElement(u"SW_SECURE_DECODE"_ns);
    144 
    145 #if defined(MOZ_WIDGET_ANDROID)
    146  // MediaDrm.isCryptoSchemeSupported only allows passing
    147  // "video/mp4" or "video/webm" for mimetype string.
    148  // See
    149  // https://developer.android.com/reference/android/media/MediaDrm.html#isCryptoSchemeSupported(java.util.UUID,
    150  // java.lang.String) for more detail.
    151  typedef struct {
    152    const nsCString& mMimeType;
    153    const nsCString& mEMECodecType;
    154    const char16_t* mCodecType;
    155    KeySystemConfig::ContainerSupport* mSupportType;
    156  } DataForValidation;
    157 
    158  DataForValidation validationList[] = {
    159      {nsCString(VIDEO_MP4), EME_CODEC_H264, java::MediaDrmProxy::AVC,
    160       &config->mMP4},
    161      {nsCString(VIDEO_MP4), EME_CODEC_VP9, java::MediaDrmProxy::AVC,
    162       &config->mMP4},
    163 #  ifdef MOZ_AV1
    164      {nsCString(VIDEO_MP4), EME_CODEC_AV1, java::MediaDrmProxy::AV1,
    165       &config->mMP4},
    166 #  endif
    167      {nsCString(AUDIO_MP4), EME_CODEC_AAC, java::MediaDrmProxy::AAC,
    168       &config->mMP4},
    169      {nsCString(AUDIO_MP4), EME_CODEC_FLAC, java::MediaDrmProxy::FLAC,
    170       &config->mMP4},
    171      {nsCString(AUDIO_MP4), EME_CODEC_OPUS, java::MediaDrmProxy::OPUS,
    172       &config->mMP4},
    173      {nsCString(VIDEO_WEBM), EME_CODEC_VP8, java::MediaDrmProxy::VP8,
    174       &config->mWebM},
    175      {nsCString(VIDEO_WEBM), EME_CODEC_VP9, java::MediaDrmProxy::VP9,
    176       &config->mWebM},
    177 #  ifdef MOZ_AV1
    178      {nsCString(VIDEO_WEBM), EME_CODEC_AV1, java::MediaDrmProxy::AV1,
    179       &config->mWebM},
    180 #  endif
    181      {nsCString(AUDIO_WEBM), EME_CODEC_VORBIS, java::MediaDrmProxy::VORBIS,
    182       &config->mWebM},
    183      {nsCString(AUDIO_WEBM), EME_CODEC_OPUS, java::MediaDrmProxy::OPUS,
    184       &config->mWebM},
    185  };
    186 
    187  for (const auto& data : validationList) {
    188    if (java::MediaDrmProxy::IsCryptoSchemeSupported(kWidevineKeySystemName,
    189                                                     data.mMimeType)) {
    190      if (!AndroidDecoderModule::SupportsMimeType(data.mMimeType).isEmpty()) {
    191        data.mSupportType->SetCanDecryptAndDecode(data.mEMECodecType);
    192      } else {
    193        data.mSupportType->SetCanDecrypt(data.mEMECodecType);
    194      }
    195    }
    196  }
    197 #else
    198 #  if defined(XP_WIN)
    199  // Widevine CDM doesn't include an AAC decoder. So if WMF can't
    200  // decode AAC, and a codec wasn't specified, be conservative
    201  // and reject the MediaKeys request, since we assume Widevine
    202  // will be used with AAC.
    203  auto pdmFactory = MakeRefPtr<PDMFactory>();
    204  if (!pdmFactory->SupportsMimeType("audio/mp4a-latm"_ns).isEmpty()) {
    205    config->mMP4.SetCanDecrypt(EME_CODEC_AAC);
    206  }
    207 #  else
    208  config->mMP4.SetCanDecrypt(EME_CODEC_AAC);
    209 #  endif
    210  config->mMP4.SetCanDecrypt(EME_CODEC_FLAC);
    211  config->mMP4.SetCanDecrypt(EME_CODEC_OPUS);
    212  config->mMP4.SetCanDecryptAndDecode(EME_CODEC_H264);
    213  config->mMP4.SetCanDecryptAndDecode(EME_CODEC_VP9);
    214 #  ifdef MOZ_AV1
    215  config->mMP4.SetCanDecryptAndDecode(EME_CODEC_AV1);
    216 #  endif
    217  config->mWebM.SetCanDecrypt(EME_CODEC_VORBIS);
    218  config->mWebM.SetCanDecrypt(EME_CODEC_OPUS);
    219  config->mWebM.SetCanDecryptAndDecode(EME_CODEC_VP8);
    220  config->mWebM.SetCanDecryptAndDecode(EME_CODEC_VP9);
    221 #  ifdef MOZ_AV1
    222  config->mWebM.SetCanDecryptAndDecode(EME_CODEC_AV1);
    223 #  endif
    224 #endif
    225 }
    226 
    227 /* static */
    228 RefPtr<KeySystemConfig::SupportedConfigsPromise>
    229 KeySystemConfig::CreateKeySystemConfigs(
    230    const nsTArray<KeySystemConfigRequest>& aRequests) {
    231  // Create available configs for all supported key systems in the request, but
    232  // some of them might not be created immediately.
    233 
    234  nsTArray<KeySystemConfig> outConfigs;
    235  nsTArray<KeySystemConfigRequest> asyncRequests;
    236 
    237  for (const auto& request : aRequests) {
    238    const nsAString& keySystem = request.mKeySystem;
    239    if (!Supports(keySystem)) {
    240      continue;
    241    }
    242 
    243    if (IsClearkeyKeySystem(keySystem)) {
    244      CreateClearKeyKeySystemConfigs(request, outConfigs);
    245    } else if (IsWidevineKeySystem(keySystem)) {
    246      CreateWivineL3KeySystemConfigs(request, outConfigs);
    247    }
    248 #ifdef MOZ_WMF_CDM
    249    else if (IsPlayReadyKeySystemAndSupported(keySystem) ||
    250             IsWidevineExperimentKeySystemAndSupported(keySystem)) {
    251      asyncRequests.AppendElement(request);
    252    }
    253 #endif
    254  }
    255 
    256 #ifdef MOZ_WMF_CDM
    257  if (!asyncRequests.IsEmpty()) {
    258    RefPtr<SupportedConfigsPromise::Private> promise =
    259        new SupportedConfigsPromise::Private(__func__);
    260    RefPtr<WMFCDMCapabilites> cdm = new WMFCDMCapabilites();
    261    cdm->GetCapabilities(asyncRequests)
    262        ->Then(GetMainThreadSerialEventTarget(), __func__,
    263               [syncConfigs = std::move(outConfigs),
    264                promise](SupportedConfigsPromise::ResolveOrRejectValue&&
    265                             aResult) mutable {
    266                 // Return the capabilities we already know
    267                 if (aResult.IsReject()) {
    268                   promise->Resolve(std::move(syncConfigs), __func__);
    269                   return;
    270                 }
    271                 // Merge sync results with async results
    272                 auto& asyncConfigs = aResult.ResolveValue();
    273                 asyncConfigs.AppendElements(std::move(syncConfigs));
    274                 promise->Resolve(std::move(asyncConfigs), __func__);
    275               });
    276    return promise;
    277  }
    278 #endif
    279  return SupportedConfigsPromise::CreateAndResolve(std::move(outConfigs),
    280                                                   __func__);
    281 }
    282 
    283 /* static */
    284 void KeySystemConfig::GetGMPKeySystemConfigs(dom::Promise* aPromise) {
    285  MOZ_ASSERT(aPromise);
    286 
    287  // Generate config requests
    288  const nsTArray<nsString> keySystemNames{
    289      NS_ConvertUTF8toUTF16(kClearKeyKeySystemName),
    290      NS_ConvertUTF8toUTF16(kWidevineKeySystemName),
    291  };
    292  nsTArray<KeySystemConfigRequest> requests;
    293  for (const auto& keySystem : keySystemNames) {
    294 #ifdef MOZ_WMF_CDM
    295    if (IsWMFClearKeySystemAndSupported(keySystem)) {
    296      // Using wmf clearkey, not gmp clearkey.
    297      continue;
    298    }
    299 #endif
    300    requests.AppendElement(KeySystemConfigRequest{
    301        keySystem, DecryptionInfo::Software, false /* IsPrivateBrowsing */});
    302  }
    303 
    304  // Get supported configs
    305  KeySystemConfig::CreateKeySystemConfigs(requests)->Then(
    306      GetMainThreadSerialEventTarget(), __func__,
    307      [promise = RefPtr<dom::Promise>{aPromise}](
    308          const SupportedConfigsPromise::ResolveOrRejectValue& aResult) {
    309        if (aResult.IsResolve()) {
    310          // Generate CDMInformation from configs
    311          FallibleTArray<dom::CDMInformation> cdmInfo;
    312          for (const auto& config : aResult.ResolveValue()) {
    313            auto* info = cdmInfo.AppendElement(fallible);
    314            if (!info) {
    315              promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
    316              return;
    317            }
    318            info->mKeySystemName = config.mKeySystem;
    319            info->mCapabilities = config.GetDebugInfo();
    320            info->mClearlead = DoesKeySystemSupportClearLead(config.mKeySystem);
    321            // GMP-based CDM doesn't support hardware decryption.
    322            info->mIsHardwareDecryption = false;
    323          }
    324          promise->MaybeResolve(cdmInfo);
    325        } else {
    326          promise->MaybeReject(NS_ERROR_DOM_MEDIA_CDM_ERR);
    327        }
    328      });
    329 }
    330 
    331 nsString KeySystemConfig::GetDebugInfo() const {
    332  nsString debugInfo;
    333  debugInfo.AppendLiteral(" key-system=");
    334  debugInfo.Append(mKeySystem);
    335  debugInfo.AppendLiteral(" init-data-type=[");
    336  for (size_t idx = 0; idx < mInitDataTypes.Length(); idx++) {
    337    debugInfo.Append(mInitDataTypes[idx]);
    338    if (idx + 1 < mInitDataTypes.Length()) {
    339      debugInfo.AppendLiteral(",");
    340    }
    341  }
    342  debugInfo.AppendLiteral("]");
    343  debugInfo.AppendPrintf(" persistent=%s", EnumValueToString(mPersistentState));
    344  debugInfo.AppendPrintf(" distinctive=%s",
    345                         EnumValueToString(mDistinctiveIdentifier));
    346  debugInfo.AppendLiteral(" sessionType=[");
    347  for (size_t idx = 0; idx < mSessionTypes.Length(); idx++) {
    348    debugInfo.AppendASCII(EnumValueToString(mSessionTypes[idx]));
    349    if (idx + 1 < mSessionTypes.Length()) {
    350      debugInfo.AppendLiteral(",");
    351    }
    352  }
    353  debugInfo.AppendLiteral("]");
    354  debugInfo.AppendLiteral(" video-robustness=");
    355  for (size_t idx = 0; idx < mVideoRobustness.Length(); idx++) {
    356    debugInfo.Append(mVideoRobustness[idx]);
    357    if (idx + 1 < mVideoRobustness.Length()) {
    358      debugInfo.AppendLiteral(",");
    359    }
    360  }
    361  debugInfo.AppendLiteral(" audio-robustness=");
    362  for (size_t idx = 0; idx < mAudioRobustness.Length(); idx++) {
    363    debugInfo.Append(mAudioRobustness[idx]);
    364    if (idx + 1 < mAudioRobustness.Length()) {
    365      debugInfo.AppendLiteral(",");
    366    }
    367  }
    368  debugInfo.AppendLiteral(" MP4={");
    369  debugInfo.Append(NS_ConvertUTF8toUTF16(mMP4.GetDebugInfo()));
    370  debugInfo.AppendLiteral("}");
    371  debugInfo.AppendLiteral(" WEBM={");
    372  debugInfo.Append(NS_ConvertUTF8toUTF16(mWebM.GetDebugInfo()));
    373  debugInfo.AppendLiteral("}");
    374  return debugInfo;
    375 }
    376 
    377 KeySystemConfig::SessionType ConvertToKeySystemConfigSessionType(
    378    dom::MediaKeySessionType aType) {
    379  switch (aType) {
    380    case dom::MediaKeySessionType::Temporary:
    381      return KeySystemConfig::SessionType::Temporary;
    382    case dom::MediaKeySessionType::Persistent_license:
    383      return KeySystemConfig::SessionType::PersistentLicense;
    384    default:
    385      MOZ_ASSERT_UNREACHABLE("Invalid session type");
    386      return KeySystemConfig::SessionType::Temporary;
    387  }
    388 }
    389 
    390 }  // namespace mozilla