Home | History | Annotate | Download | only in crypto
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/renderer/media/crypto/key_systems.h"
      6 
      7 #include <map>
      8 #include <string>
      9 
     10 #include "base/lazy_instance.h"
     11 #include "base/logging.h"
     12 #include "base/strings/string_util.h"
     13 #include "content/public/common/content_client.h"
     14 #include "content/public/renderer/content_renderer_client.h"
     15 #include "content/public/renderer/key_system_info.h"
     16 #include "content/renderer/media/crypto/key_systems_support_uma.h"
     17 #include "net/base/mime_util.h"
     18 #include "third_party/WebKit/public/platform/WebCString.h"
     19 #include "third_party/WebKit/public/platform/WebString.h"
     20 
     21 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
     22 
     23 namespace content {
     24 
     25 const char kClearKeyKeySystem[] = "webkit-org.w3.clearkey";
     26 
     27 const char kAudioWebM[] = "audio/webm";
     28 const char kVideoWebM[] = "video/webm";
     29 const char kVorbis[] = "vorbis";
     30 const char kVorbisVP8[] = "vorbis,vp8,vp8.0";
     31 
     32 #if defined(USE_PROPRIETARY_CODECS)
     33 const char kAudioMp4[] = "audio/mp4";
     34 const char kVideoMp4[] = "video/mp4";
     35 const char kMp4a[] = "mp4a";
     36 const char kMp4aAvc1Avc3[] = "mp4a,avc1,avc3";
     37 #endif  // defined(USE_PROPRIETARY_CODECS)
     38 
     39 #if !defined(GOOGLE_TV)
     40 inline std::string KeySystemNameForUMAInternal(
     41     const blink::WebString& key_system) {
     42   if (key_system == kClearKeyKeySystem)
     43     return "ClearKey";
     44 #if defined(WIDEVINE_CDM_AVAILABLE)
     45   if (key_system == kWidevineKeySystem)
     46     return "Widevine";
     47 #endif  // WIDEVINE_CDM_AVAILABLE
     48   return "Unknown";
     49 }
     50 #else
     51 // Declares the function, which is defined in another file.
     52 std::string KeySystemNameForUMAInternal(const blink::WebString& key_system);
     53 #endif  // !defined(GOOGLE_TV)
     54 
     55 // Convert a WebString to ASCII, falling back on an empty string in the case
     56 // of a non-ASCII string.
     57 static std::string ToASCIIOrEmpty(const blink::WebString& string) {
     58   return IsStringASCII(string) ? UTF16ToASCII(string) : std::string();
     59 }
     60 
     61 static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) {
     62   KeySystemInfo info(kClearKeyKeySystem);
     63 
     64   info.supported_types.push_back(std::make_pair(kAudioWebM, kVorbis));
     65   info.supported_types.push_back(std::make_pair(kVideoWebM, kVorbisVP8));
     66 #if defined(USE_PROPRIETARY_CODECS)
     67   info.supported_types.push_back(std::make_pair(kAudioMp4, kMp4a));
     68   info.supported_types.push_back(std::make_pair(kVideoMp4, kMp4aAvc1Avc3));
     69 #endif  // defined(USE_PROPRIETARY_CODECS)
     70 
     71   info.use_aes_decryptor = true;
     72 
     73   concrete_key_systems->push_back(info);
     74 }
     75 
     76 class KeySystems {
     77  public:
     78   static KeySystems& GetInstance();
     79 
     80   bool IsConcreteSupportedKeySystem(const std::string& key_system);
     81 
     82   bool IsSupportedKeySystemWithMediaMimeType(
     83       const std::string& mime_type,
     84       const std::vector<std::string>& codecs,
     85       const std::string& key_system);
     86 
     87   bool UseAesDecryptor(const std::string& concrete_key_system);
     88 
     89 #if defined(ENABLE_PEPPER_CDMS)
     90   std::string GetPepperType(const std::string& concrete_key_system);
     91 #elif defined(OS_ANDROID)
     92   std::vector<uint8> GetUUID(const std::string& concrete_key_system);
     93 #endif
     94 
     95  private:
     96   void AddConcreteSupportedKeySystems(
     97       const std::vector<KeySystemInfo>& concrete_key_systems);
     98 
     99   void AddConcreteSupportedKeySystem(
    100       const std::string& key_system,
    101       bool use_aes_decryptor,
    102 #if defined(ENABLE_PEPPER_CDMS)
    103       const std::string& pepper_type,
    104 #elif defined(OS_ANDROID)
    105       const std::vector<uint8>& uuid,
    106 #endif
    107       const std::vector<KeySystemInfo::ContainerCodecsPair>& supported_types,
    108       const std::string& parent_key_system);
    109 
    110 
    111   friend struct base::DefaultLazyInstanceTraits<KeySystems>;
    112 
    113   typedef base::hash_set<std::string> CodecSet;
    114   typedef std::map<std::string, CodecSet> MimeTypeMap;
    115 
    116   struct KeySystemProperties {
    117     KeySystemProperties() : use_aes_decryptor(false) {}
    118 
    119     bool use_aes_decryptor;
    120 #if defined(ENABLE_PEPPER_CDMS)
    121     std::string pepper_type;
    122 #elif defined(OS_ANDROID)
    123     std::vector<uint8> uuid;
    124 #endif
    125     MimeTypeMap types;
    126   };
    127 
    128   typedef std::map<std::string, KeySystemProperties> KeySystemPropertiesMap;
    129 
    130   typedef std::map<std::string, std::string> ParentKeySystemMap;
    131 
    132   KeySystems();
    133   ~KeySystems() {}
    134 
    135   void AddSupportedType(const std::string& mime_type,
    136                         const std::string& codecs_list,
    137                         KeySystemProperties* properties);
    138 
    139   bool IsSupportedKeySystemWithContainerAndCodec(const std::string& mime_type,
    140                                                  const std::string& codec,
    141                                                  const std::string& key_system);
    142 
    143   // Map from key system string to capabilities.
    144   KeySystemPropertiesMap concrete_key_system_map_;
    145 
    146   // Map from parent key system to the concrete key system that should be used
    147   // to represent its capabilities.
    148   ParentKeySystemMap parent_key_system_map_;
    149 
    150   KeySystemsSupportUMA key_systems_support_uma_;
    151 
    152   DISALLOW_COPY_AND_ASSIGN(KeySystems);
    153 };
    154 
    155 static base::LazyInstance<KeySystems> g_key_systems = LAZY_INSTANCE_INITIALIZER;
    156 
    157 KeySystems& KeySystems::GetInstance() {
    158   return g_key_systems.Get();
    159 }
    160 
    161 // Because we use a LazyInstance, the key systems info must be populated when
    162 // the instance is lazily initiated.
    163 KeySystems::KeySystems() {
    164   std::vector<KeySystemInfo> key_systems_info;
    165   GetContentClient()->renderer()->AddKeySystems(&key_systems_info);
    166   // Clear Key is always supported.
    167   AddClearKey(&key_systems_info);
    168   AddConcreteSupportedKeySystems(key_systems_info);
    169 #if defined(WIDEVINE_CDM_AVAILABLE)
    170   key_systems_support_uma_.AddKeySystemToReport(kWidevineKeySystem);
    171 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
    172 }
    173 
    174 void KeySystems::AddConcreteSupportedKeySystems(
    175     const std::vector<KeySystemInfo>& concrete_key_systems) {
    176   for (size_t i = 0; i < concrete_key_systems.size(); ++i) {
    177     const KeySystemInfo& key_system_info = concrete_key_systems[i];
    178     AddConcreteSupportedKeySystem(key_system_info.key_system,
    179                                   key_system_info.use_aes_decryptor,
    180 #if defined(ENABLE_PEPPER_CDMS)
    181                                   key_system_info.pepper_type,
    182 #elif defined(OS_ANDROID)
    183                                   key_system_info.uuid,
    184 #endif
    185                                   key_system_info.supported_types,
    186                                   key_system_info.parent_key_system);
    187   }
    188 }
    189 
    190 void KeySystems::AddConcreteSupportedKeySystem(
    191     const std::string& concrete_key_system,
    192     bool use_aes_decryptor,
    193 #if defined(ENABLE_PEPPER_CDMS)
    194     const std::string& pepper_type,
    195 #elif defined(OS_ANDROID)
    196     const std::vector<uint8>& uuid,
    197 #endif
    198     const std::vector<KeySystemInfo::ContainerCodecsPair>& supported_types,
    199     const std::string& parent_key_system) {
    200   DCHECK(!IsConcreteSupportedKeySystem(concrete_key_system))
    201       << "Key system '" << concrete_key_system << "' already registered";
    202   DCHECK(parent_key_system_map_.find(concrete_key_system) ==
    203          parent_key_system_map_.end())
    204       <<  "'" << concrete_key_system << " is already registered as a parent";
    205 
    206   KeySystemProperties properties;
    207   properties.use_aes_decryptor = use_aes_decryptor;
    208 #if defined(ENABLE_PEPPER_CDMS)
    209   DCHECK_EQ(use_aes_decryptor, pepper_type.empty());
    210   properties.pepper_type = pepper_type;
    211 #elif defined(OS_ANDROID)
    212   DCHECK_EQ(use_aes_decryptor, uuid.empty());
    213   DCHECK(use_aes_decryptor || uuid.size() == 16);
    214   properties.uuid = uuid;
    215 #endif
    216 
    217   for (size_t i = 0; i < supported_types.size(); ++i) {
    218     const KeySystemInfo::ContainerCodecsPair& pair = supported_types[i];
    219     const std::string& mime_type = pair.first;
    220     const std::string& codecs_list = pair.second;
    221     AddSupportedType(mime_type, codecs_list, &properties);
    222   }
    223 
    224   concrete_key_system_map_[concrete_key_system] = properties;
    225 
    226   if (!parent_key_system.empty()) {
    227     DCHECK(!IsConcreteSupportedKeySystem(parent_key_system))
    228         << "Parent '" << parent_key_system << "' already registered concrete";
    229     DCHECK(parent_key_system_map_.find(parent_key_system) ==
    230            parent_key_system_map_.end())
    231         << "Parent '" << parent_key_system << "' already registered";
    232     parent_key_system_map_[parent_key_system] = concrete_key_system;
    233   }
    234 }
    235 
    236 void KeySystems::AddSupportedType(const std::string& mime_type,
    237                                   const std::string& codecs_list,
    238                                   KeySystemProperties* properties) {
    239   std::vector<std::string> mime_type_codecs;
    240   net::ParseCodecString(codecs_list, &mime_type_codecs, false);
    241 
    242   CodecSet codecs(mime_type_codecs.begin(), mime_type_codecs.end());
    243 
    244   MimeTypeMap& mime_types_map = properties->types;
    245   // mime_types_map must not be repeated for a given key system.
    246   DCHECK(mime_types_map.find(mime_type) == mime_types_map.end());
    247   mime_types_map[mime_type] = codecs;
    248 }
    249 
    250 bool KeySystems::IsConcreteSupportedKeySystem(const std::string& key_system) {
    251   return concrete_key_system_map_.find(key_system) !=
    252       concrete_key_system_map_.end();
    253 }
    254 
    255 bool KeySystems::IsSupportedKeySystemWithContainerAndCodec(
    256     const std::string& mime_type,
    257     const std::string& codec,
    258     const std::string& key_system) {
    259   bool has_type = !mime_type.empty();
    260   DCHECK(has_type || codec.empty());
    261 
    262   key_systems_support_uma_.ReportKeySystemQuery(key_system, has_type);
    263 
    264   KeySystemPropertiesMap::const_iterator key_system_iter =
    265       concrete_key_system_map_.find(key_system);
    266   if (key_system_iter == concrete_key_system_map_.end())
    267     return false;
    268 
    269   key_systems_support_uma_.ReportKeySystemSupport(key_system, false);
    270 
    271   if (mime_type.empty())
    272     return true;
    273 
    274   const MimeTypeMap& mime_types_map = key_system_iter->second.types;
    275   MimeTypeMap::const_iterator mime_iter = mime_types_map.find(mime_type);
    276   if (mime_iter == mime_types_map.end())
    277     return false;
    278 
    279   if (codec.empty()) {
    280     key_systems_support_uma_.ReportKeySystemSupport(key_system, true);
    281     return true;
    282   }
    283 
    284   const CodecSet& codecs = mime_iter->second;
    285   if (codecs.find(codec) == codecs.end())
    286     return false;
    287 
    288   key_systems_support_uma_.ReportKeySystemSupport(key_system, true);
    289   return true;
    290 }
    291 
    292 bool KeySystems::IsSupportedKeySystemWithMediaMimeType(
    293     const std::string& mime_type,
    294     const std::vector<std::string>& codecs,
    295     const std::string& key_system) {
    296   // If |key_system| is a parent key_system, use its concrete child.
    297   // Otherwise, use |key_system|.
    298   std::string concrete_key_system;
    299   ParentKeySystemMap::iterator parent_key_system_iter =
    300       parent_key_system_map_.find(key_system);
    301   if (parent_key_system_iter != parent_key_system_map_.end())
    302     concrete_key_system = parent_key_system_iter->second;
    303   else
    304     concrete_key_system = key_system;
    305 
    306   if (codecs.empty()) {
    307     return IsSupportedKeySystemWithContainerAndCodec(
    308         mime_type, std::string(), concrete_key_system);
    309   }
    310 
    311   for (size_t i = 0; i < codecs.size(); ++i) {
    312     if (!IsSupportedKeySystemWithContainerAndCodec(
    313              mime_type, codecs[i], concrete_key_system)) {
    314       return false;
    315     }
    316   }
    317 
    318   return true;
    319 }
    320 
    321 bool KeySystems::UseAesDecryptor(const std::string& concrete_key_system) {
    322   KeySystemPropertiesMap::iterator key_system_iter =
    323       concrete_key_system_map_.find(concrete_key_system);
    324   if (key_system_iter == concrete_key_system_map_.end()) {
    325       DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
    326       return false;
    327   }
    328 
    329   return key_system_iter->second.use_aes_decryptor;
    330 }
    331 
    332 #if defined(ENABLE_PEPPER_CDMS)
    333 std::string KeySystems::GetPepperType(const std::string& concrete_key_system) {
    334   KeySystemPropertiesMap::iterator key_system_iter =
    335       concrete_key_system_map_.find(concrete_key_system);
    336   if (key_system_iter == concrete_key_system_map_.end()) {
    337       DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
    338       return std::string();
    339   }
    340 
    341   const std::string& type = key_system_iter->second.pepper_type;
    342   DLOG_IF(FATAL, type.empty()) << concrete_key_system << " is not Pepper-based";
    343   return type;
    344 }
    345 #elif defined(OS_ANDROID)
    346 std::vector<uint8> KeySystems::GetUUID(const std::string& concrete_key_system) {
    347   KeySystemPropertiesMap::iterator key_system_iter =
    348       concrete_key_system_map_.find(concrete_key_system);
    349   if (key_system_iter == concrete_key_system_map_.end()) {
    350       DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
    351       return std::vector<uint8>();
    352   }
    353 
    354   return key_system_iter->second.uuid;
    355 }
    356 #endif
    357 
    358 //------------------------------------------------------------------------------
    359 
    360 bool IsConcreteSupportedKeySystem(const blink::WebString& key_system) {
    361   return KeySystems::GetInstance().IsConcreteSupportedKeySystem(
    362       ToASCIIOrEmpty(key_system));
    363 }
    364 
    365 bool IsSupportedKeySystemWithMediaMimeType(
    366     const std::string& mime_type,
    367     const std::vector<std::string>& codecs,
    368     const std::string& key_system) {
    369   return KeySystems::GetInstance().IsSupportedKeySystemWithMediaMimeType(
    370       mime_type, codecs, key_system);
    371 }
    372 
    373 std::string KeySystemNameForUMA(const blink::WebString& key_system) {
    374   return KeySystemNameForUMAInternal(key_system);
    375 }
    376 
    377 std::string KeySystemNameForUMA(const std::string& key_system) {
    378   return KeySystemNameForUMAInternal(blink::WebString::fromUTF8(key_system));
    379 }
    380 
    381 bool CanUseAesDecryptor(const std::string& concrete_key_system) {
    382   return KeySystems::GetInstance().UseAesDecryptor(concrete_key_system);
    383 }
    384 
    385 #if defined(ENABLE_PEPPER_CDMS)
    386 std::string GetPepperType(const std::string& concrete_key_system) {
    387   return KeySystems::GetInstance().GetPepperType(concrete_key_system);
    388 }
    389 #elif defined(OS_ANDROID)
    390 std::vector<uint8> GetUUID(const std::string& concrete_key_system) {
    391   return KeySystems::GetInstance().GetUUID(concrete_key_system);
    392 }
    393 #endif
    394 
    395 }  // namespace content
    396