diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp index 8c807a93cbd2..4494f400742e 100644 --- a/dom/media/eme/MediaKeySystemAccess.cpp +++ b/dom/media/eme/MediaKeySystemAccess.cpp @@ -443,6 +443,9 @@ GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService, for (const nsString& candidate : aCandidate.mInitDataTypes.Value()) { if (candidate.EqualsLiteral("cenc")) { initDataTypes.AppendElement(candidate); + } else if (candidate.EqualsLiteral("keyids") && + aKeySystem.EqualsLiteral("org.w3.clearkey")) { + initDataTypes.AppendElement(candidate); } } if (initDataTypes.IsEmpty()) { diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini index 315178163e82..c58988e62483 100644 --- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -619,6 +619,8 @@ skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'go [test_eme_session_callable_value.html] [test_eme_canvas_blocked.html] skip-if = toolkit == 'android' # bug 1149374 +[test_eme_key_ids_initdata.html] +skip-if = toolkit == 'android' # bug 1149374 [test_eme_non_mse_fails.html] skip-if = toolkit == 'android' # bug 1149374 [test_eme_request_notifications.html] diff --git a/dom/media/test/test_eme_key_ids_initdata.html b/dom/media/test/test_eme_key_ids_initdata.html new file mode 100644 index 000000000000..ba688648d9de --- /dev/null +++ b/dom/media/test/test_eme_key_ids_initdata.html @@ -0,0 +1,111 @@ + + + + Test Encrypted Media Extensions + + + + + + +
+
+
+ + diff --git a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp index 93df08a0c15b..2e14d822ee46 100644 --- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp @@ -113,12 +113,14 @@ public: CreateSessionTask(ClearKeySessionManager* aTarget, uint32_t aCreateSessionToken, uint32_t aPromiseId, + const string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize, GMPSessionType aSessionType) : mTarget(aTarget) , mCreateSessionToken(aCreateSessionToken) , mPromiseId(aPromiseId) + , mInitDataType(aInitDataType) , mSessionType(aSessionType) { mInitData.insert(mInitData.end(), @@ -128,8 +130,8 @@ public: virtual void Run() override { mTarget->CreateSession(mCreateSessionToken, mPromiseId, - "cenc", - strlen("cenc"), + mInitDataType.c_str(), + mInitDataType.size(), &mInitData.front(), mInitData.size(), mSessionType); @@ -141,6 +143,7 @@ private: RefPtr mTarget; uint32_t mCreateSessionToken; uint32_t mPromiseId; + const string mInitDataType; vector mInitData; GMPSessionType mSessionType; }; @@ -150,6 +153,7 @@ private: ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance, uint32_t aCreateSessionToken, uint32_t aPromiseId, + const string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize, GMPSessionType aSessionType) @@ -160,6 +164,7 @@ ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeySessionManager* aInsta GMPTask* t = new CreateSessionTask(aInstance, aCreateSessionToken, aPromiseId, + aInitDataType, aInitData, aInitDataSize, aSessionType); diff --git a/media/gmp-clearkey/0.1/ClearKeyPersistence.h b/media/gmp-clearkey/0.1/ClearKeyPersistence.h index 3cbb3513ce93..ddab9f783c1d 100644 --- a/media/gmp-clearkey/0.1/ClearKeyPersistence.h +++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h @@ -31,6 +31,7 @@ public: static bool DeferCreateSessionIfNotReady(ClearKeySessionManager* aInstance, uint32_t aCreateSessionToken, uint32_t aPromiseId, + const std::string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize, GMPSessionType aSessionType); diff --git a/media/gmp-clearkey/0.1/ClearKeySession.cpp b/media/gmp-clearkey/0.1/ClearKeySession.cpp index e414ce110e93..8dcad9afa1f1 100644 --- a/media/gmp-clearkey/0.1/ClearKeySession.cpp +++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp @@ -54,13 +54,25 @@ ClearKeySession::~ClearKeySession() void ClearKeySession::Init(uint32_t aCreateSessionToken, uint32_t aPromiseId, + const std::string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize) { CK_LOGD("ClearKeySession::Init"); - ClearKeyUtils::ParseInitData(aInitData, aInitDataSize, mKeyIds); + if (aInitDataType == "cenc") { + ClearKeyUtils::ParseCENCInitData(aInitData, aInitDataSize, mKeyIds); + } else if (aInitDataType == "keyids") { + std::string sessionType; + ClearKeyUtils::ParseKeyIdsInitData(aInitData, aInitDataSize, mKeyIds, sessionType); + if (sessionType != ClearKeyUtils::SessionTypeToString(mSessionType)) { + const char message[] = "Session type specified in keyids init data doesn't match session type."; + mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message)); + return; + } + } + if (!mKeyIds.size()) { - const char message[] = "Couldn't parse cenc key init data"; + const char message[] = "Couldn't parse init data"; mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message)); return; } diff --git a/media/gmp-clearkey/0.1/ClearKeySession.h b/media/gmp-clearkey/0.1/ClearKeySession.h index 14886d3227b8..2783b2253b19 100644 --- a/media/gmp-clearkey/0.1/ClearKeySession.h +++ b/media/gmp-clearkey/0.1/ClearKeySession.h @@ -38,6 +38,7 @@ public: void Init(uint32_t aCreateSessionToken, uint32_t aPromiseId, + const string& aInitDataType, const uint8_t* aInitData, uint32_t aInitDataSize); GMPSessionType Type() const; diff --git a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp index 17cb8ced1ba1..6a089c265856 100644 --- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp +++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp @@ -101,8 +101,9 @@ ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken, { CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType); - // initDataType must be "cenc". - if (strcmp("cenc", aInitDataType)) { + string initDataType(aInitDataType, aInitDataType + aInitDataTypeSize); + // initDataType must be "cenc" or "keyids". + if (initDataType != "cenc" && initDataType != "keyids") { mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError, nullptr /* message */, 0 /* messageLen */); return; @@ -111,6 +112,7 @@ ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken, if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this, aCreateSessionToken, aPromiseId, + initDataType, aInitData, aInitDataSize, aSessionType)) { @@ -121,7 +123,7 @@ ClearKeySessionManager::CreateSession(uint32_t aCreateSessionToken, assert(mSessions.find(sessionId) == mSessions.end()); ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType); - session->Init(aCreateSessionToken, aPromiseId, aInitData, aInitDataSize); + session->Init(aCreateSessionToken, aPromiseId, initDataType, aInitData, aInitDataSize); mSessions[sessionId] = session; const vector& sessionKeys = session->GetKeyIds(); diff --git a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp index b98674335b0c..ef4697699e2e 100644 --- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp +++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp @@ -132,8 +132,9 @@ EncodeBase64Web(vector aBinary, string& aEncoded) } /* static */ void -ClearKeyUtils::ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize, - vector& aOutKeys) +ClearKeyUtils::ParseCENCInitData(const uint8_t* aInitData, + uint32_t aInitDataSize, + vector& aOutKeyIds) { using mozilla::BigEndian; @@ -182,7 +183,7 @@ ClearKeyUtils::ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize, } for (uint32_t i = 0; i < kidCount; i++) { - aOutKeys.push_back(KeyId(data, data + CLEARKEY_KEY_LEN)); + aOutKeyIds.push_back(KeyId(data, data + CLEARKEY_KEY_LEN)); data += CLEARKEY_KEY_LEN; } } @@ -195,7 +196,7 @@ ClearKeyUtils::MakeKeyRequest(const vector& aKeyIDs, { assert(aKeyIDs.size() && aOutRequest.empty()); - aOutRequest.append("{ \"kids\":["); + aOutRequest.append("{\"kids\":["); for (size_t i = 0; i < aKeyIDs.size(); i++) { if (i) { aOutRequest.append(","); @@ -208,7 +209,7 @@ ClearKeyUtils::MakeKeyRequest(const vector& aKeyIDs, aOutRequest.append("\""); } - aOutRequest.append("], \"type\":"); + aOutRequest.append("],\"type\":"); aOutRequest.append("\""); aOutRequest.append(SessionTypeToString(aSessionType)); @@ -508,6 +509,80 @@ ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize, return true; } +static bool +ParseKeyIds(ParserContext& aCtx, vector& aOutKeyIds) +{ + // Consume start of array. + EXPECT_SYMBOL(aCtx, '['); + + while (true) { + string label; + vector keyId; + if (!GetNextLabel(aCtx, label) || + !DecodeBase64KeyOrId(label, keyId)) { + return false; + } + assert(!keyId.empty()); + aOutKeyIds.push_back(keyId); + + uint8_t sym = PeekSymbol(aCtx); + if (!sym || sym == ']') { + break; + } + + EXPECT_SYMBOL(aCtx, ','); + } + + return GetNextSymbol(aCtx) == ']'; +} + + +/* static */ bool +ClearKeyUtils::ParseKeyIdsInitData(const uint8_t* aInitData, + uint32_t aInitDataSize, + vector& aOutKeyIds, + string& aOutSessionType) +{ + aOutSessionType = "temporary"; + + ParserContext ctx; + ctx.mIter = aInitData; + ctx.mEnd = aInitData + aInitDataSize; + + // Consume '{' from start of object. + EXPECT_SYMBOL(ctx, '{'); + + while (true) { + string label; + // Consume member kids. + if (!GetNextLabel(ctx, label)) return false; + EXPECT_SYMBOL(ctx, ':'); + + if (label == "kids") { + // Parse "kids" array. + if (!ParseKeyIds(ctx, aOutKeyIds)) return false; + } else if (label == "type") { + // Consume type string. + if (!GetNextLabel(ctx, aOutSessionType)) return false; + } else { + SkipToken(ctx); + } + + // Check for end of object. + if (PeekSymbol(ctx) == '}') { + break; + } + + // Consume ',' between object members. + EXPECT_SYMBOL(ctx, ','); + } + + // Consume '}' from end of object. + EXPECT_SYMBOL(ctx, '}'); + + return true; +} + /* static */ const char* ClearKeyUtils::SessionTypeToString(GMPSessionType aSessionType) { diff --git a/media/gmp-clearkey/0.1/ClearKeyUtils.h b/media/gmp-clearkey/0.1/ClearKeyUtils.h index 6b70c6cebbcc..fc10b20c7cb9 100644 --- a/media/gmp-clearkey/0.1/ClearKeyUtils.h +++ b/media/gmp-clearkey/0.1/ClearKeyUtils.h @@ -54,8 +54,14 @@ public: static void DecryptAES(const std::vector& aKey, std::vector& aData, std::vector& aIV); - static void ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize, - std::vector& aOutKeys); + static void ParseCENCInitData(const uint8_t* aInitData, + uint32_t aInitDataSize, + std::vector& aOutKeyIds); + + static bool ParseKeyIdsInitData(const uint8_t* aInitData, + uint32_t aInitDataSize, + std::vector& aOutKeyIds, + std::string& aOutSessionType); static void MakeKeyRequest(const std::vector& aKeyIds, std::string& aOutRequest,