Bug 1308076 - Use PsshParser to validate CENC init data. r=jwwang draft
authorChris Pearce <cpearce@mozilla.com>
Thu, 06 Oct 2016 23:17:44 +1300
changeset 423461 2b3efcbe3245e76762362a054c093a09ff663fca
parent 423460 4dc7cf2be0092d3de076b2f8becc942de6e363dd
child 423462 782e42542809abaabb0accefbd9d889a17b2ee13
push id31912
push userbmo:cpearce@mozilla.com
push dateTue, 11 Oct 2016 08:02:34 +0000
reviewersjwwang
bugs1308076
milestone52.0a1
Bug 1308076 - Use PsshParser to validate CENC init data. r=jwwang Now that we can link gmp-clearkey's PSSH parser into Gecko, we can simply use that inside MediaKeySession to validate that the CENC init data matches the spec. This change enforces that CENC init data uses the common system Id. As far as I can tell, Widevine only uses that now. MozReview-Commit-ID: HrlKQHcv5DI
dom/media/eme/MediaKeySession.cpp
media/psshparser/PsshParser.cpp
media/psshparser/PsshParser.h
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -15,16 +15,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/CDMProxy.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/Move.h"
 #include "nsContentUtils.h"
 #include "mozilla/EMEUtils.h"
 #include "GMPUtils.h"
 #include "nsPrintfCString.h"
+#include "psshparser/PsshParser.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaKeySession,
                                    DOMEventTargetHelper,
                                    mMediaKeyError,
                                    mKeys,
@@ -187,17 +188,18 @@ ValidateInitData(const nsTArray<uint8_t>
   if (aInitDataType.LowerCaseEqualsLiteral("webm")) {
     // WebM initData consists of a single keyId. Ensure it's of reasonable length.
     return aInitData.Length() <= MAX_KEY_ID_LENGTH;
   } else if (aInitDataType.LowerCaseEqualsLiteral("cenc")) {
     // Limit initData to less than 64KB.
     if (aInitData.Length() > MAX_CENC_INIT_DATA_LENGTH) {
       return false;
     }
-    // TODO: Validate PSSH in future patch...
+    std::vector<std::vector<uint8_t>> keyIds;
+    return ParseCENCInitData(aInitData.Elements(), aInitData.Length(), keyIds);
   } else if (aInitDataType.LowerCaseEqualsLiteral("keyids")) {
     if (aInitData.Length() > MAX_KEY_ID_LENGTH) {
       return false;
     }
     // Ensure that init data matches the expected JSON format.
     mozilla::dom::KeyIdsInitData keyIds;
     nsString json;
     nsDependentCSubstring raw(reinterpret_cast<const char*>(aInitData.Elements()), aInitData.Length());
--- a/media/psshparser/PsshParser.cpp
+++ b/media/psshparser/PsshParser.cpp
@@ -102,87 +102,91 @@ private:
 
  // System ID identifying the cenc v2 pssh box format; specified at:
  // https://dvcs.w3.org/hg/html-media/raw-file/tip/encrypted-media/cenc-format.html
 const uint8_t kSystemID[] = {
   0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
   0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
 };
 
-void
+bool
 ParseCENCInitData(const uint8_t* aInitData,
                   uint32_t aInitDataSize,
                   std::vector<std::vector<uint8_t>>& aOutKeyIds)
 {
   ByteReader reader(aInitData, aInitDataSize);
   while (reader.CanRead32()) {
     // Box size. For the common system Id, ignore this, as some useragents
     // handle invalid box sizes.
     const size_t start = reader.Offset();
     const size_t size = reader.ReadU32();
     if (size > std::numeric_limits<size_t>::max() - start) {
       // Ensure 'start + size' calculation below can't overflow.
-      return;
+      return false;
     }
-    const size_t end = std::min<size_t>(start + size, reader.Length());
+    const size_t end = start + size;
+    if (end > reader.Length()) {
+      // Ridiculous sized box.
+      return false;
+    }
 
     // PSSH box type.
     if (!reader.CanRead32()) {
-      return;
+      return false;
     }
     uint32_t box = reader.ReadU32();
     if (box != FOURCC('p','s','s','h')) {
-      reader.Seek(std::max<size_t>(reader.Offset(), end));
-      continue;
+      return false;
     }
 
     // 1 byte version, 3 bytes flags.
     if (!reader.CanRead32()) {
-      return;
+      return false;
     }
     uint8_t version = reader.ReadU8();
     if (version != 1) {
       // Ignore pssh boxes with wrong version.
       reader.Seek(std::max<size_t>(reader.Offset(), end));
       continue;
     }
     reader.Read(3); // skip flags.
 
     // SystemID
     const uint8_t* sid = reader.Read(sizeof(kSystemID));
     if (!sid) {
-      // Insufficinet bytes to read SystemID.
-      return;
+      // Insufficient bytes to read SystemID.
+      return false;
     }
     if (memcmp(kSystemID, sid, sizeof(kSystemID))) {
       // Ignore pssh boxes with wrong system ID.
       reader.Seek(std::max<size_t>(reader.Offset(), end));
       continue;
     }
 
     if (!reader.CanRead32()) {
-      return;
+      return false;
     }
     uint32_t kidCount = reader.ReadU32();
 
     for (uint32_t i = 0; i < kidCount; i++) {
       if (reader.Remaining() < CLEARKEY_KEY_LEN) {
         // Not enough remaining to read key.
-        return;
+        return false;
       }
       const uint8_t* kid = reader.Read(CLEARKEY_KEY_LEN);
       aOutKeyIds.push_back(std::vector<uint8_t>(kid, kid + CLEARKEY_KEY_LEN));
     }
 
     // Size of extra data. EME CENC format spec says datasize should
     // always be 0. We explicitly read the datasize, in case the box
     // size was 0, so that we get to the end of the box.
     if (!reader.CanRead32()) {
-      return;
+      return false;
     }
     reader.ReadU32();
 
     // Jump forwards to the end of the box, skipping any padding.
     if (size) {
       reader.Seek(end);
     }
   }
+  return true;
 }
--- a/media/psshparser/PsshParser.h
+++ b/media/psshparser/PsshParser.h
@@ -17,14 +17,14 @@
 #ifndef __ClearKeyCencParser_h__
 #define __ClearKeyCencParser_h__
 
 #include <stdint.h>
 #include <vector>
 
 #define CLEARKEY_KEY_LEN ((size_t)16)
 
-void
+bool
 ParseCENCInitData(const uint8_t* aInitData,
                   uint32_t aInitDataSize,
                   std::vector<std::vector<uint8_t>>& aOutKeyIds);
 
 #endif