mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 05:45:37 +00:00
Bug 1109457 - Add persistent session to our ClearKey CDM. r=edwin
This commit is contained in:
parent
c6bc5e0ec5
commit
65cccb08b1
@ -118,6 +118,7 @@ class GMPRecordClient {
|
|||||||
class GMPRecordIterator {
|
class GMPRecordIterator {
|
||||||
public:
|
public:
|
||||||
// Retrieve the name for the current record.
|
// Retrieve the name for the current record.
|
||||||
|
// aOutName is null terminated at character at index (*aOutNameLength).
|
||||||
// Returns GMPNoErr if successful, or GMPEndOfEnumeration if iteration has
|
// Returns GMPNoErr if successful, or GMPEndOfEnumeration if iteration has
|
||||||
// reached the end.
|
// reached the end.
|
||||||
virtual GMPErr GetName(const char ** aOutName, uint32_t * aOutNameLength) = 0;
|
virtual GMPErr GetName(const char ** aOutName, uint32_t * aOutNameLength) = 0;
|
||||||
|
@ -3,9 +3,12 @@ const KEYSYSTEM_TYPE = "org.w3.clearkey";
|
|||||||
function bail(message)
|
function bail(message)
|
||||||
{
|
{
|
||||||
return function(err) {
|
return function(err) {
|
||||||
|
if (err) {
|
||||||
|
message += "; " + String(err)
|
||||||
|
}
|
||||||
ok(false, message);
|
ok(false, message);
|
||||||
if (err) {
|
if (err) {
|
||||||
info(err);
|
info(String(err));
|
||||||
}
|
}
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
@ -70,13 +73,13 @@ function Log(token, msg) {
|
|||||||
info(TimeStamp(token) + " " + msg);
|
info(TimeStamp(token) + " " + msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
function UpdateSessionFunc(test, token) {
|
function UpdateSessionFunc(test, token, sessionType) {
|
||||||
return function(ev) {
|
return function(ev) {
|
||||||
var msgStr = ArrayBufferToString(ev.message);
|
var msgStr = ArrayBufferToString(ev.message);
|
||||||
var msg = JSON.parse(msgStr);
|
var msg = JSON.parse(msgStr);
|
||||||
|
|
||||||
Log(token, "got message from CDM: " + msgStr);
|
Log(token, "got message from CDM: " + msgStr);
|
||||||
is(msg.type, test.sessionType, TimeStamp(token) + " key session type should match");
|
is(msg.type, sessionType, TimeStamp(token) + " key session type should match");
|
||||||
ok(msg.kids, TimeStamp(token) + " message event should contain key ID array");
|
ok(msg.kids, TimeStamp(token) + " message event should contain key ID array");
|
||||||
|
|
||||||
var outKeys = [];
|
var outKeys = [];
|
||||||
@ -211,24 +214,24 @@ function SetupEME(test, token, params)
|
|||||||
.then(function(keySystemAccess) {
|
.then(function(keySystemAccess) {
|
||||||
return keySystemAccess.createMediaKeys();
|
return keySystemAccess.createMediaKeys();
|
||||||
}, bail(token + " Failed to request key system access."))
|
}, bail(token + " Failed to request key system access."))
|
||||||
|
|
||||||
.then(function(mediaKeys) {
|
.then(function(mediaKeys) {
|
||||||
Log(token, "created MediaKeys object ok");
|
Log(token, "created MediaKeys object ok");
|
||||||
mediaKeys.sessions = [];
|
mediaKeys.sessions = [];
|
||||||
return v.setMediaKeys(mediaKeys);
|
return v.setMediaKeys(mediaKeys);
|
||||||
}, bail("failed to create MediaKeys object"))
|
}, bail("failed to create MediaKeys object"))
|
||||||
|
|
||||||
.then(function() {
|
.then(function() {
|
||||||
Log(token, "set MediaKeys on <video> element ok");
|
Log(token, "set MediaKeys on <video> element ok");
|
||||||
|
var sessionType = (params && params.sessionType) ? params.sessionType : "temporary";
|
||||||
var session = v.mediaKeys.createSession(test.sessionType);
|
var session = v.mediaKeys.createSession(sessionType);
|
||||||
if (params && params.onsessioncreated) {
|
if (params && params.onsessioncreated) {
|
||||||
params.onsessioncreated(session);
|
params.onsessioncreated(session);
|
||||||
}
|
}
|
||||||
session.addEventListener("message", UpdateSessionFunc(test, token));
|
session.addEventListener("message", UpdateSessionFunc(test, token, sessionType));
|
||||||
return session.generateRequest(ev.initDataType, ev.initData);
|
return session.generateRequest(ev.initDataType, ev.initData);
|
||||||
}, onSetKeysFail)
|
}, onSetKeysFail)
|
||||||
|
|
||||||
.then(function() {
|
.then(function() {
|
||||||
Log(token, "generated request");
|
Log(token, "generated request");
|
||||||
}, bail(token + " Failed to request key system access2."));
|
}, bail(token + " Failed to request key system access2."));
|
||||||
|
@ -364,6 +364,8 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
|
|||||||
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
|
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
|
||||||
[test_eme_canvas_blocked.html]
|
[test_eme_canvas_blocked.html]
|
||||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||||
|
[test_eme_persistent_sessions.html]
|
||||||
|
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||||
[test_eme_playback.html]
|
[test_eme_playback.html]
|
||||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
|
||||||
[test_eme_requestKeySystemAccess.html]
|
[test_eme_requestKeySystemAccess.html]
|
||||||
|
156
dom/media/test/test_eme_persistent_sessions.html
Normal file
156
dom/media/test/test_eme_persistent_sessions.html
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test Encrypted Media Extensions</title>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||||
|
<script type="text/javascript" src="manifest.js"></script>
|
||||||
|
<script type="text/javascript" src="eme.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
var manager = new MediaTestManager;
|
||||||
|
|
||||||
|
function UsableKeyIdsMatch(usableKeyIds, expectedKeyIds) {
|
||||||
|
var hexKeyIds = usableKeyIds.map(function(keyId) {
|
||||||
|
return Base64ToHex(window.btoa(ArrayBufferToString(keyId)));
|
||||||
|
}).sort();
|
||||||
|
var expected = Object.keys(expectedKeyIds).sort();
|
||||||
|
if (expected.length != hexKeyIds.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < hexKeyIds.length; i++) {
|
||||||
|
if (hexKeyIds[i] != expected[i]){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AwaitAllKeysUsable(session, keys, token) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
function listener(event) {
|
||||||
|
session.getUsableKeyIds().then(function(usableKeyIds) {
|
||||||
|
var u = UsableKeyIdsMatch(usableKeyIds, keys);
|
||||||
|
if (UsableKeyIdsMatch(usableKeyIds, keys)) {
|
||||||
|
Log(token, "resolving AwaitAllKeysUsable promise");
|
||||||
|
session.removeEventListener("keyschange", listener);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}, bail(token + " failed to get usableKeyIds"));
|
||||||
|
}
|
||||||
|
session.addEventListener("keyschange", listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function AwaitAllKeysNotUsable(session, token) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
function listener(event) {
|
||||||
|
session.getUsableKeyIds().then(function(usableKeyIds) {
|
||||||
|
if (usableKeyIds.length == 0) {
|
||||||
|
session.removeEventListener("keyschange", listener);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}, bail(token + " failed to get usableKeyIds"));
|
||||||
|
}
|
||||||
|
session.addEventListener("keyschange", listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function startTest(test, token)
|
||||||
|
{
|
||||||
|
manager.started(token);
|
||||||
|
|
||||||
|
var recreatedSession; // will have remove() called on it.
|
||||||
|
|
||||||
|
var keySystemAccess;
|
||||||
|
|
||||||
|
var v = SetupEME(test, token,
|
||||||
|
{
|
||||||
|
onsessioncreated: function(session) {
|
||||||
|
Log(token, "Session created");
|
||||||
|
var sessionId;
|
||||||
|
initialSession = session;
|
||||||
|
|
||||||
|
// Once the session has loaded and has all its keys usable, close
|
||||||
|
// all sessions without calling remove() on them.
|
||||||
|
AwaitAllKeysUsable(session, test.keys, token).then(
|
||||||
|
function() {
|
||||||
|
sessionId = session.sessionId;
|
||||||
|
Log(token, "Closing session with id=" + sessionId);
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Once the session is closed, reload the MediaKeys and reload the session
|
||||||
|
session.closed.then(function() {
|
||||||
|
return navigator.requestMediaKeySystemAccess(KEYSYSTEM_TYPE)
|
||||||
|
}, bail("close promise rejected"))
|
||||||
|
|
||||||
|
.then(function(requestedKeySystemAccess) {
|
||||||
|
keySystemAccess = requestedKeySystemAccess;
|
||||||
|
return keySystemAccess.createMediaKeys();
|
||||||
|
}, bail(token + " Failed to request key system access."))
|
||||||
|
|
||||||
|
.then(function(mediaKeys) {
|
||||||
|
Log(token, "re-created MediaKeys object ok");
|
||||||
|
recreatedSession = mediaKeys.createSession("persistent");
|
||||||
|
Log(token, "Created recreatedSession, loading sessionId=" + sessionId);
|
||||||
|
return Promise.all([AwaitAllKeysUsable(recreatedSession, test.keys, token), recreatedSession.load(sessionId)]);
|
||||||
|
}, bail(token + " failed to create mediaKeys"))
|
||||||
|
|
||||||
|
.then(function() {
|
||||||
|
Log(token, "re-loaded persistent session, all keys still usable");
|
||||||
|
return Promise.all([AwaitAllKeysNotUsable(recreatedSession, token), recreatedSession.remove()]);
|
||||||
|
}, bail(token + " failed to get reload session or keys"))
|
||||||
|
|
||||||
|
.then(function() {
|
||||||
|
Log(token, "removed session, all keys unusable.");
|
||||||
|
// Attempt to recreate the session, the attempt should fail.
|
||||||
|
return keySystemAccess.createMediaKeys();
|
||||||
|
}, bail(token + " failed to remove session"))
|
||||||
|
|
||||||
|
.then(function(mediaKeys) {
|
||||||
|
Log(token, "re-re-created MediaKeys object ok");
|
||||||
|
// Trying to load the removed persistent session should fail.
|
||||||
|
return mediaKeys.createSession("persistent").load(sessionId);
|
||||||
|
}, bail(token + " failed to create mediaKeys"))
|
||||||
|
|
||||||
|
.then(function(suceeded) {
|
||||||
|
is(suceeded, false, token + " we expect the third session creation to fail, as the session should have been removed.");
|
||||||
|
manager.finished(token);
|
||||||
|
}, bail(token + " failure to load session."));
|
||||||
|
|
||||||
|
},
|
||||||
|
sessionType: "persistent",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
v.addEventListener("error", bail(token + " got error event"));
|
||||||
|
|
||||||
|
LoadTest(test, v, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
function beginTest() {
|
||||||
|
manager.runTests(gEMETests, startTest);
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefs = [
|
||||||
|
[ "media.mediasource.enabled", true ],
|
||||||
|
[ "media.mediasource.mp4.enabled", true ],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (/Linux/.test(navigator.userAgent) ||
|
||||||
|
!document.createElement('video').canPlayType("video/mp4")) {
|
||||||
|
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
|
||||||
|
prefs.push([ "media.fragmented-mp4.exposed", true ]);
|
||||||
|
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -2,12 +2,14 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "ClearKeyDecryptionManager.h"
|
#include "ClearKeyDecryptionManager.h"
|
||||||
#include "ClearKeyUtils.h"
|
#include "ClearKeyUtils.h"
|
||||||
|
#include "ClearKeyStorage.h"
|
||||||
|
#include "ClearKeyPersistence.h"
|
||||||
|
#include "gmp-task-utils.h"
|
||||||
|
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/NullPtr.h"
|
#include "mozilla/NullPtr.h"
|
||||||
@ -15,7 +17,6 @@
|
|||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
class ClearKeyDecryptor
|
class ClearKeyDecryptor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -29,6 +30,8 @@ public:
|
|||||||
uint32_t AddRef();
|
uint32_t AddRef();
|
||||||
uint32_t Release();
|
uint32_t Release();
|
||||||
|
|
||||||
|
const Key& DecryptionKey() const { return mKey; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct DecryptTask : public GMPTask
|
struct DecryptTask : public GMPTask
|
||||||
{
|
{
|
||||||
@ -79,15 +82,22 @@ private:
|
|||||||
Key mKey;
|
Key mKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
ClearKeyDecryptionManager::ClearKeyDecryptionManager()
|
ClearKeyDecryptionManager::ClearKeyDecryptionManager()
|
||||||
{
|
{
|
||||||
CK_LOGD("ClearKeyDecryptionManager ctor");
|
CK_LOGD("ClearKeyDecryptionManager ctor");
|
||||||
|
|
||||||
|
// The ClearKeyDecryptionManager maintains a self reference which is
|
||||||
|
// removed when the host is finished with the interface and calls
|
||||||
|
// DecryptingComplete(). We make ClearKeyDecryptionManager refcounted so
|
||||||
|
// that the tasks to that we dispatch to call functions on it won't end up
|
||||||
|
// derefing a null reference after DecryptingComplete() is called.
|
||||||
|
AddRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
|
ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
|
||||||
{
|
{
|
||||||
CK_LOGD("ClearKeyDecryptionManager dtor");
|
CK_LOGD("ClearKeyDecryptionManager dtor");
|
||||||
|
MOZ_ASSERT(mRefCount == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -97,19 +107,7 @@ ClearKeyDecryptionManager::Init(GMPDecryptorCallback* aCallback)
|
|||||||
mCallback = aCallback;
|
mCallback = aCallback;
|
||||||
mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO |
|
mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO |
|
||||||
GMP_EME_CAP_DECRYPT_VIDEO);
|
GMP_EME_CAP_DECRYPT_VIDEO);
|
||||||
}
|
ClearKeyPersistence::EnsureInitialized();
|
||||||
|
|
||||||
static string
|
|
||||||
GetNewSessionId()
|
|
||||||
{
|
|
||||||
static uint32_t sNextSessionId = 0;
|
|
||||||
|
|
||||||
string sessionId;
|
|
||||||
stringstream ss;
|
|
||||||
ss << ++sNextSessionId;
|
|
||||||
ss >> sessionId;
|
|
||||||
|
|
||||||
return sessionId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -129,10 +127,18 @@ ClearKeyDecryptionManager::CreateSession(uint32_t aPromiseId,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string sessionId = GetNewSessionId();
|
if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
|
||||||
|
aPromiseId,
|
||||||
|
aInitData,
|
||||||
|
aInitDataSize,
|
||||||
|
aSessionType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType);
|
||||||
MOZ_ASSERT(mSessions.find(sessionId) == mSessions.end());
|
MOZ_ASSERT(mSessions.find(sessionId) == mSessions.end());
|
||||||
|
|
||||||
ClearKeySession* session = new ClearKeySession(sessionId, mCallback);
|
ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
|
||||||
session->Init(aPromiseId, aInitData, aInitDataSize);
|
session->Init(aPromiseId, aInitData, aInitDataSize);
|
||||||
mSessions[sessionId] = session;
|
mSessions[sessionId] = session;
|
||||||
|
|
||||||
@ -157,7 +163,7 @@ ClearKeyDecryptionManager::CreateSession(uint32_t aPromiseId,
|
|||||||
|
|
||||||
// Send a request for needed key data.
|
// Send a request for needed key data.
|
||||||
string request;
|
string request;
|
||||||
ClearKeyUtils::MakeKeyRequest(neededKeys, request);
|
ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType);
|
||||||
mCallback->SessionMessage(&sessionId[0], sessionId.length(),
|
mCallback->SessionMessage(&sessionId[0], sessionId.length(),
|
||||||
(uint8_t*)&request[0], request.length(),
|
(uint8_t*)&request[0], request.length(),
|
||||||
"" /* destination url */, 0);
|
"" /* destination url */, 0);
|
||||||
@ -168,10 +174,73 @@ ClearKeyDecryptionManager::LoadSession(uint32_t aPromiseId,
|
|||||||
const char* aSessionId,
|
const char* aSessionId,
|
||||||
uint32_t aSessionIdLength)
|
uint32_t aSessionIdLength)
|
||||||
{
|
{
|
||||||
// TODO implement "persistent" sessions.
|
|
||||||
mCallback->ResolveLoadSessionPromise(aPromiseId, false);
|
|
||||||
|
|
||||||
CK_LOGD("ClearKeyDecryptionManager::LoadSession");
|
CK_LOGD("ClearKeyDecryptionManager::LoadSession");
|
||||||
|
|
||||||
|
if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
|
||||||
|
mCallback->ResolveLoadSessionPromise(aPromiseId, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
|
||||||
|
aPromiseId,
|
||||||
|
aSessionId,
|
||||||
|
aSessionIdLength)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string sid(aSessionId, aSessionId + aSessionIdLength);
|
||||||
|
if (!ClearKeyPersistence::IsPersistentSessionId(sid)) {
|
||||||
|
mCallback->ResolveLoadSessionPromise(aPromiseId, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callsback PersistentSessionDataLoaded with results...
|
||||||
|
ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearKeyDecryptionManager::PersistentSessionDataLoaded(GMPErr aStatus,
|
||||||
|
uint32_t aPromiseId,
|
||||||
|
const string& aSessionId,
|
||||||
|
const uint8_t* aKeyData,
|
||||||
|
uint32_t aKeyDataSize)
|
||||||
|
{
|
||||||
|
if (GMP_FAILED(aStatus) ||
|
||||||
|
Contains(mSessions, aSessionId) ||
|
||||||
|
(aKeyDataSize % (2 * CLEARKEY_KEY_LEN)) != 0) {
|
||||||
|
mCallback->ResolveLoadSessionPromise(aPromiseId, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearKeySession* session = new ClearKeySession(aSessionId,
|
||||||
|
mCallback,
|
||||||
|
kGMPPersistentSession);
|
||||||
|
mSessions[aSessionId] = session;
|
||||||
|
|
||||||
|
// TODO: currently we have to resolve the load-session promise before we
|
||||||
|
// can mark the keys as usable. We should really do this before marking
|
||||||
|
// the keys usable, but we need to fix Gecko first.
|
||||||
|
mCallback->ResolveLoadSessionPromise(aPromiseId, true);
|
||||||
|
|
||||||
|
uint32_t numKeys = aKeyDataSize / (2 * CLEARKEY_KEY_LEN);
|
||||||
|
for (uint32_t i = 0; i < numKeys; i ++) {
|
||||||
|
const uint8_t* base = aKeyData + 2 * CLEARKEY_KEY_LEN * i;
|
||||||
|
|
||||||
|
KeyId keyId(base, base + CLEARKEY_KEY_LEN);
|
||||||
|
MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
|
||||||
|
|
||||||
|
Key key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN);
|
||||||
|
MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
|
||||||
|
|
||||||
|
session->AddKeyId(keyId);
|
||||||
|
|
||||||
|
if (!Contains(mDecryptors, keyId)) {
|
||||||
|
mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, key);
|
||||||
|
}
|
||||||
|
mDecryptors[keyId]->AddRef();
|
||||||
|
mCallback->KeyIdUsable(aSessionId.c_str(), aSessionId.size(),
|
||||||
|
&keyId[0], keyId.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -184,20 +253,21 @@ ClearKeyDecryptionManager::UpdateSession(uint32_t aPromiseId,
|
|||||||
CK_LOGD("ClearKeyDecryptionManager::UpdateSession");
|
CK_LOGD("ClearKeyDecryptionManager::UpdateSession");
|
||||||
string sessionId(aSessionId, aSessionId + aSessionIdLength);
|
string sessionId(aSessionId, aSessionId + aSessionIdLength);
|
||||||
|
|
||||||
if (mSessions.find(sessionId) == mSessions.end() || !mSessions[sessionId]) {
|
auto itr = mSessions.find(sessionId);
|
||||||
|
if (itr == mSessions.end() || !(itr->second)) {
|
||||||
CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
|
CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
|
||||||
mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
|
mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
ClearKeySession* session = itr->second;
|
||||||
|
|
||||||
// Parse the response for any (key ID, key) pairs.
|
// Parse the response for any (key ID, key) pairs.
|
||||||
vector<KeyIdPair> keyPairs;
|
vector<KeyIdPair> keyPairs;
|
||||||
if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs)) {
|
if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) {
|
||||||
CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
|
CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
|
||||||
mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, nullptr, 0);
|
mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, nullptr, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mCallback->ResolvePromise(aPromiseId);
|
|
||||||
|
|
||||||
for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) {
|
for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) {
|
||||||
KeyId& keyId = it->mKeyId;
|
KeyId& keyId = it->mKeyId;
|
||||||
@ -210,6 +280,43 @@ ClearKeyDecryptionManager::UpdateSession(uint32_t aPromiseId,
|
|||||||
|
|
||||||
mDecryptors[keyId]->AddRef();
|
mDecryptors[keyId]->AddRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (session->Type() != kGMPPersistentSession) {
|
||||||
|
mCallback->ResolvePromise(aPromiseId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the keys on disk. We store a record whose name is the sessionId,
|
||||||
|
// and simply append each keyId followed by its key.
|
||||||
|
vector<uint8_t> keydata;
|
||||||
|
Serialize(session, keydata);
|
||||||
|
GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
|
||||||
|
static const char* message = "Couldn't store cenc key init data";
|
||||||
|
GMPTask* reject = WrapTask(mCallback,
|
||||||
|
&GMPDecryptorCallback::RejectPromise,
|
||||||
|
aPromiseId,
|
||||||
|
kGMPInvalidStateError,
|
||||||
|
message,
|
||||||
|
strlen(message));
|
||||||
|
StoreData(sessionId, keydata, resolve, reject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearKeyDecryptionManager::Serialize(const ClearKeySession* aSession,
|
||||||
|
std::vector<uint8_t>& aOutKeyData)
|
||||||
|
{
|
||||||
|
const std::vector<KeyId>& keyIds = aSession->GetKeyIds();
|
||||||
|
for (size_t i = 0; i < keyIds.size(); i++) {
|
||||||
|
const KeyId& keyId = keyIds[i];
|
||||||
|
if (!Contains(mDecryptors, keyId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
|
||||||
|
aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end());
|
||||||
|
const Key& key = mDecryptors[keyId]->DecryptionKey();
|
||||||
|
MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
|
||||||
|
aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -220,25 +327,39 @@ ClearKeyDecryptionManager::CloseSession(uint32_t aPromiseId,
|
|||||||
CK_LOGD("ClearKeyDecryptionManager::CloseSession");
|
CK_LOGD("ClearKeyDecryptionManager::CloseSession");
|
||||||
|
|
||||||
string sessionId(aSessionId, aSessionId + aSessionIdLength);
|
string sessionId(aSessionId, aSessionId + aSessionIdLength);
|
||||||
ClearKeySession* session = mSessions[sessionId];
|
auto itr = mSessions.find(sessionId);
|
||||||
|
if (itr == mSessions.end()) {
|
||||||
|
CK_LOGW("ClearKey CDM couldn't close non-existent session.");
|
||||||
|
mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearKeySession* session = itr->second;
|
||||||
MOZ_ASSERT(session);
|
MOZ_ASSERT(session);
|
||||||
|
|
||||||
const vector<KeyId>& keyIds = session->GetKeyIds();
|
ClearInMemorySessionData(session);
|
||||||
|
mCallback->ResolvePromise(aPromiseId);
|
||||||
|
mCallback->SessionClosed(aSessionId, aSessionIdLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearKeyDecryptionManager::ClearInMemorySessionData(ClearKeySession* aSession)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aSession);
|
||||||
|
|
||||||
|
const vector<KeyId>& keyIds = aSession->GetKeyIds();
|
||||||
for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
|
for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
|
||||||
MOZ_ASSERT(mDecryptors.find(*it) != mDecryptors.end());
|
MOZ_ASSERT(mDecryptors.find(*it) != mDecryptors.end());
|
||||||
|
|
||||||
if (!mDecryptors[*it]->Release()) {
|
if (!mDecryptors[*it]->Release()) {
|
||||||
mDecryptors.erase(*it);
|
mDecryptors.erase(*it);
|
||||||
mCallback->KeyIdNotUsable(aSessionId, aSessionIdLength,
|
mCallback->KeyIdNotUsable(aSession->Id().c_str(), aSession->Id().size(),
|
||||||
&(*it)[0], it->size());
|
&(*it)[0], it->size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mSessions.erase(sessionId);
|
mSessions.erase(aSession->Id());
|
||||||
delete session;
|
delete aSession;
|
||||||
|
|
||||||
mCallback->ResolvePromise(aPromiseId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -246,10 +367,39 @@ ClearKeyDecryptionManager::RemoveSession(uint32_t aPromiseId,
|
|||||||
const char* aSessionId,
|
const char* aSessionId,
|
||||||
uint32_t aSessionIdLength)
|
uint32_t aSessionIdLength)
|
||||||
{
|
{
|
||||||
// TODO implement "persistent" sessions.
|
string sessionId(aSessionId, aSessionId + aSessionIdLength);
|
||||||
CK_LOGD("ClearKeyDecryptionManager::RemoveSession");
|
auto itr = mSessions.find(sessionId);
|
||||||
mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError,
|
if (itr == mSessions.end()) {
|
||||||
nullptr /* message */, 0 /* messageLen */);
|
CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
|
||||||
|
mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearKeySession* session = itr->second;
|
||||||
|
MOZ_ASSERT(session);
|
||||||
|
string sid = session->Id();
|
||||||
|
bool isPersistent = session->Type() == kGMPPersistentSession;
|
||||||
|
ClearInMemorySessionData(session);
|
||||||
|
|
||||||
|
if (!isPersistent) {
|
||||||
|
mCallback->ResolvePromise(aPromiseId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearKeyPersistence::PersistentSessionRemoved(sid);
|
||||||
|
|
||||||
|
// Overwrite the record storing the sessionId's key data with a zero
|
||||||
|
// length record to delete it.
|
||||||
|
vector<uint8_t> emptyKeydata;
|
||||||
|
GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
|
||||||
|
static const char* message = "Could not remove session";
|
||||||
|
GMPTask* reject = WrapTask(mCallback,
|
||||||
|
&GMPDecryptorCallback::RejectPromise,
|
||||||
|
aPromiseId,
|
||||||
|
kGMPInvalidAccessError,
|
||||||
|
message,
|
||||||
|
strlen(message));
|
||||||
|
StoreData(sessionId, emptyKeydata, resolve, reject);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -270,8 +420,11 @@ ClearKeyDecryptionManager::Decrypt(GMPBuffer* aBuffer,
|
|||||||
CK_LOGD("ClearKeyDecryptionManager::Decrypt");
|
CK_LOGD("ClearKeyDecryptionManager::Decrypt");
|
||||||
KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize());
|
KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize());
|
||||||
|
|
||||||
if (mDecryptors.find(keyId) == mDecryptors.end() || !mDecryptors[keyId]) {
|
auto itr = mDecryptors.find(keyId);
|
||||||
|
if (itr == mDecryptors.end() || !(itr->second)) {
|
||||||
|
CK_LOGD("ClearKeyDecryptionManager::Decrypt GMPNoKeyErr");
|
||||||
mCallback->Decrypted(aBuffer, GMPNoKeyErr);
|
mCallback->Decrypted(aBuffer, GMPNoKeyErr);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mDecryptors[keyId]->QueueDecrypt(aBuffer, aMetadata);
|
mDecryptors[keyId]->QueueDecrypt(aBuffer, aMetadata);
|
||||||
@ -290,7 +443,7 @@ ClearKeyDecryptionManager::DecryptingComplete()
|
|||||||
delete it->second;
|
delete it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this;
|
Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -13,14 +13,14 @@
|
|||||||
#include "ClearKeyUtils.h"
|
#include "ClearKeyUtils.h"
|
||||||
#include "gmp-api/gmp-decryption.h"
|
#include "gmp-api/gmp-decryption.h"
|
||||||
#include "ScopedNSSTypes.h"
|
#include "ScopedNSSTypes.h"
|
||||||
|
#include "RefCounted.h"
|
||||||
|
|
||||||
class ClearKeyDecryptor;
|
class ClearKeyDecryptor;
|
||||||
|
|
||||||
class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor
|
class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor
|
||||||
|
, public RefCounted
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClearKeyDecryptionManager();
|
ClearKeyDecryptionManager();
|
||||||
~ClearKeyDecryptionManager();
|
|
||||||
|
|
||||||
virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
|
virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
|
||||||
|
|
||||||
@ -58,7 +58,18 @@ public:
|
|||||||
|
|
||||||
virtual void DecryptingComplete() MOZ_OVERRIDE;
|
virtual void DecryptingComplete() MOZ_OVERRIDE;
|
||||||
|
|
||||||
|
void PersistentSessionDataLoaded(GMPErr aStatus,
|
||||||
|
uint32_t aPromiseId,
|
||||||
|
const std::string& aSessionId,
|
||||||
|
const uint8_t* aKeyData,
|
||||||
|
uint32_t aKeyDataSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
~ClearKeyDecryptionManager();
|
||||||
|
|
||||||
|
void ClearInMemorySessionData(ClearKeySession* aSession);
|
||||||
|
void Serialize(const ClearKeySession* aSession, std::vector<uint8_t>& aOutKeyData);
|
||||||
|
|
||||||
GMPDecryptorCallback* mCallback;
|
GMPDecryptorCallback* mCallback;
|
||||||
|
|
||||||
std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
|
std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
|
||||||
|
236
media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
Normal file
236
media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "ClearKeyPersistence.h"
|
||||||
|
#include "ClearKeyUtils.h"
|
||||||
|
#include "ClearKeyStorage.h"
|
||||||
|
#include "ClearKeyDecryptionManager.h"
|
||||||
|
#include "mozilla/RefPtr.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// Whether we've loaded the persistent session ids from GMPStorage yet.
|
||||||
|
enum PersistentKeyState {
|
||||||
|
UNINITIALIZED,
|
||||||
|
LOADING,
|
||||||
|
LOADED
|
||||||
|
};
|
||||||
|
static PersistentKeyState sPersistentKeyState = UNINITIALIZED;
|
||||||
|
|
||||||
|
// Set of session Ids of the persistent sessions created or residing in
|
||||||
|
// storage.
|
||||||
|
static set<uint32_t> sPersistentSessionIds;
|
||||||
|
|
||||||
|
static vector<GMPTask*> sTasksBlockedOnSessionIdLoad;
|
||||||
|
|
||||||
|
static void
|
||||||
|
ReadAllRecordsFromIterator(GMPRecordIterator* aRecordIterator,
|
||||||
|
void* aUserArg,
|
||||||
|
GMPErr aStatus)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(sPersistentKeyState == LOADING);
|
||||||
|
if (GMP_SUCCEEDED(aStatus)) {
|
||||||
|
// Extract the record names which are valid uint32_t's; they're
|
||||||
|
// the persistent session ids.
|
||||||
|
const char* name = nullptr;
|
||||||
|
uint32_t len = 0;
|
||||||
|
while (GMP_SUCCEEDED(aRecordIterator->GetName(&name, &len))) {
|
||||||
|
if (ClearKeyUtils::IsValidSessionId(name, len)) {
|
||||||
|
MOZ_ASSERT(name[len] == 0);
|
||||||
|
sPersistentSessionIds.insert(atoi(name));
|
||||||
|
}
|
||||||
|
aRecordIterator->NextRecord();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sPersistentKeyState = LOADED;
|
||||||
|
aRecordIterator->Close();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sTasksBlockedOnSessionIdLoad.size(); i++) {
|
||||||
|
sTasksBlockedOnSessionIdLoad[i]->Run();
|
||||||
|
sTasksBlockedOnSessionIdLoad[i]->Destroy();
|
||||||
|
}
|
||||||
|
sTasksBlockedOnSessionIdLoad.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
ClearKeyPersistence::EnsureInitialized()
|
||||||
|
{
|
||||||
|
if (sPersistentKeyState == UNINITIALIZED) {
|
||||||
|
sPersistentKeyState = LOADING;
|
||||||
|
if (GMP_FAILED(EnumRecordNames(&ReadAllRecordsFromIterator))) {
|
||||||
|
sPersistentKeyState = LOADED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ string
|
||||||
|
ClearKeyPersistence::GetNewSessionId(GMPSessionType aSessionType)
|
||||||
|
{
|
||||||
|
static uint32_t sNextSessionId = 1;
|
||||||
|
|
||||||
|
// Ensure we don't re-use a session id that was persisted.
|
||||||
|
while (Contains(sPersistentSessionIds, sNextSessionId)) {
|
||||||
|
sNextSessionId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
string sessionId;
|
||||||
|
stringstream ss;
|
||||||
|
ss << sNextSessionId;
|
||||||
|
ss >> sessionId;
|
||||||
|
|
||||||
|
if (aSessionType == kGMPPersistentSession) {
|
||||||
|
sPersistentSessionIds.insert(sNextSessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
sNextSessionId++;
|
||||||
|
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CreateSessionTask : public GMPTask {
|
||||||
|
public:
|
||||||
|
CreateSessionTask(ClearKeyDecryptionManager* aTarget,
|
||||||
|
uint32_t aPromiseId,
|
||||||
|
const uint8_t* aInitData,
|
||||||
|
uint32_t aInitDataSize,
|
||||||
|
GMPSessionType aSessionType)
|
||||||
|
: mTarget(aTarget)
|
||||||
|
, mPromiseId(aPromiseId)
|
||||||
|
, mSessionType(aSessionType)
|
||||||
|
{
|
||||||
|
mInitData.insert(mInitData.end(),
|
||||||
|
aInitData,
|
||||||
|
aInitData + aInitDataSize);
|
||||||
|
}
|
||||||
|
virtual void Run() MOZ_OVERRIDE {
|
||||||
|
mTarget->CreateSession(mPromiseId,
|
||||||
|
"cenc",
|
||||||
|
strlen("cenc"),
|
||||||
|
&mInitData.front(),
|
||||||
|
mInitData.size(),
|
||||||
|
mSessionType);
|
||||||
|
}
|
||||||
|
virtual void Destroy() MOZ_OVERRIDE {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
RefPtr<ClearKeyDecryptionManager> mTarget;
|
||||||
|
uint32_t mPromiseId;
|
||||||
|
vector<uint8_t> mInitData;
|
||||||
|
GMPSessionType mSessionType;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
|
||||||
|
uint32_t aPromiseId,
|
||||||
|
const uint8_t* aInitData,
|
||||||
|
uint32_t aInitDataSize,
|
||||||
|
GMPSessionType aSessionType)
|
||||||
|
{
|
||||||
|
if (sPersistentKeyState >= LOADED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GMPTask* t = new CreateSessionTask(aInstance,
|
||||||
|
aPromiseId,
|
||||||
|
aInitData,
|
||||||
|
aInitDataSize,
|
||||||
|
aSessionType);
|
||||||
|
sTasksBlockedOnSessionIdLoad.push_back(t);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadSessionTask : public GMPTask {
|
||||||
|
public:
|
||||||
|
LoadSessionTask(ClearKeyDecryptionManager* aTarget,
|
||||||
|
uint32_t aPromiseId,
|
||||||
|
const char* aSessionId,
|
||||||
|
uint32_t aSessionIdLength)
|
||||||
|
: mTarget(aTarget)
|
||||||
|
, mPromiseId(aPromiseId)
|
||||||
|
, mSessionId(aSessionId, aSessionId + aSessionIdLength)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual void Run() MOZ_OVERRIDE {
|
||||||
|
mTarget->LoadSession(mPromiseId,
|
||||||
|
mSessionId.c_str(),
|
||||||
|
mSessionId.size());
|
||||||
|
}
|
||||||
|
virtual void Destroy() MOZ_OVERRIDE {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
RefPtr<ClearKeyDecryptionManager> mTarget;
|
||||||
|
uint32_t mPromiseId;
|
||||||
|
string mSessionId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
|
||||||
|
uint32_t aPromiseId,
|
||||||
|
const char* aSessionId,
|
||||||
|
uint32_t aSessionIdLength)
|
||||||
|
{
|
||||||
|
if (sPersistentKeyState >= LOADED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GMPTask* t = new LoadSessionTask(aInstance,
|
||||||
|
aPromiseId,
|
||||||
|
aSessionId,
|
||||||
|
aSessionIdLength);
|
||||||
|
sTasksBlockedOnSessionIdLoad.push_back(t);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId)
|
||||||
|
{
|
||||||
|
return Contains(sPersistentSessionIds, atoi(aSessionId.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoadSessionFromKeysTask : public ReadContinuation {
|
||||||
|
public:
|
||||||
|
LoadSessionFromKeysTask(ClearKeyDecryptionManager* aTarget,
|
||||||
|
const string& aSessionId,
|
||||||
|
uint32_t aPromiseId)
|
||||||
|
: mTarget(aTarget)
|
||||||
|
, mSessionId(aSessionId)
|
||||||
|
, mPromiseId(aPromiseId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void ReadComplete(GMPErr aStatus,
|
||||||
|
const uint8_t* aData,
|
||||||
|
uint32_t aLength) MOZ_OVERRIDE
|
||||||
|
{
|
||||||
|
mTarget->PersistentSessionDataLoaded(aStatus, mPromiseId, mSessionId, aData, aLength);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
RefPtr<ClearKeyDecryptionManager> mTarget;
|
||||||
|
string mSessionId;
|
||||||
|
uint32_t mPromiseId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
ClearKeyPersistence::LoadSessionData(ClearKeyDecryptionManager* aInstance,
|
||||||
|
const string& aSid,
|
||||||
|
uint32_t aPromiseId)
|
||||||
|
{
|
||||||
|
LoadSessionFromKeysTask* loadTask =
|
||||||
|
new LoadSessionFromKeysTask(aInstance, aSid, aPromiseId);
|
||||||
|
ReadData(aSid, loadTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void
|
||||||
|
ClearKeyPersistence::PersistentSessionRemoved(const string& aSessionId)
|
||||||
|
{
|
||||||
|
sPersistentSessionIds.erase(atoi(aSessionId.c_str()));
|
||||||
|
}
|
39
media/gmp-clearkey/0.1/ClearKeyPersistence.h
Normal file
39
media/gmp-clearkey/0.1/ClearKeyPersistence.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef __ClearKeyPersistence_h__
|
||||||
|
#define __ClearKeyPersistence_h__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "gmp-decryption.h"
|
||||||
|
|
||||||
|
class ClearKeyDecryptionManager;
|
||||||
|
|
||||||
|
class ClearKeyPersistence {
|
||||||
|
public:
|
||||||
|
static void EnsureInitialized();
|
||||||
|
|
||||||
|
static std::string GetNewSessionId(GMPSessionType aSessionType);
|
||||||
|
|
||||||
|
static bool DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
|
||||||
|
uint32_t aPromiseId,
|
||||||
|
const uint8_t* aInitData,
|
||||||
|
uint32_t aInitDataSize,
|
||||||
|
GMPSessionType aSessionType);
|
||||||
|
|
||||||
|
static bool DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
|
||||||
|
uint32_t aPromiseId,
|
||||||
|
const char* aSessionId,
|
||||||
|
uint32_t aSessionIdLength);
|
||||||
|
|
||||||
|
static bool IsPersistentSessionId(const std::string& aSid);
|
||||||
|
|
||||||
|
static void LoadSessionData(ClearKeyDecryptionManager* aInstance,
|
||||||
|
const std::string& aSid,
|
||||||
|
uint32_t aPromiseId);
|
||||||
|
|
||||||
|
static void PersistentSessionRemoved(const std::string& aSid);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __ClearKeyPersistence_h__
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "ClearKeySession.h"
|
#include "ClearKeySession.h"
|
||||||
#include "ClearKeyUtils.h"
|
#include "ClearKeyUtils.h"
|
||||||
|
#include "ClearKeyStorage.h"
|
||||||
|
#include "gmp-task-utils.h"
|
||||||
|
|
||||||
#include "gmp-api/gmp-decryption.h"
|
#include "gmp-api/gmp-decryption.h"
|
||||||
#include "mozilla/Endian.h"
|
#include "mozilla/Endian.h"
|
||||||
@ -12,9 +14,11 @@
|
|||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
|
||||||
ClearKeySession::ClearKeySession(const std::string& aSessionId,
|
ClearKeySession::ClearKeySession(const std::string& aSessionId,
|
||||||
GMPDecryptorCallback* aCallback)
|
GMPDecryptorCallback* aCallback,
|
||||||
|
GMPSessionType aSessionType)
|
||||||
: mSessionId(aSessionId)
|
: mSessionId(aSessionId)
|
||||||
, mCallback(aCallback)
|
, mCallback(aCallback)
|
||||||
|
, mSessionType(aSessionType)
|
||||||
{
|
{
|
||||||
CK_LOGD("ClearKeySession ctor %p", this);
|
CK_LOGD("ClearKeySession ctor %p", this);
|
||||||
}
|
}
|
||||||
@ -36,7 +40,18 @@ ClearKeySession::Init(uint32_t aPromiseId,
|
|||||||
mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message));
|
mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mCallback->ResolveNewSessionPromise(aPromiseId,
|
mCallback->ResolveNewSessionPromise(aPromiseId,
|
||||||
mSessionId.data(), mSessionId.length());
|
mSessionId.data(), mSessionId.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GMPSessionType
|
||||||
|
ClearKeySession::Type() const
|
||||||
|
{
|
||||||
|
return mSessionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearKeySession::AddKeyId(const KeyId& aKeyId)
|
||||||
|
{
|
||||||
|
mKeyIds.push_back(aKeyId);
|
||||||
|
}
|
||||||
|
@ -6,34 +6,39 @@
|
|||||||
#define __ClearKeySession_h__
|
#define __ClearKeySession_h__
|
||||||
|
|
||||||
#include "ClearKeyUtils.h"
|
#include "ClearKeyUtils.h"
|
||||||
|
#include "gmp-decryption.h"
|
||||||
|
|
||||||
class GMPBuffer;
|
class GMPBuffer;
|
||||||
class GMPDecryptorCallback;
|
class GMPDecryptorCallback;
|
||||||
class GMPDecryptorHost;
|
class GMPDecryptorHost;
|
||||||
class GMPEncryptedBufferMetadata;
|
class GMPEncryptedBufferMetadata;
|
||||||
|
|
||||||
/**
|
|
||||||
* Currently useless; will be fleshed out later with support for persistent
|
|
||||||
* key sessions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class ClearKeySession
|
class ClearKeySession
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClearKeySession(const std::string& aSessionId,
|
explicit ClearKeySession(const std::string& aSessionId,
|
||||||
GMPDecryptorCallback* aCallback);
|
GMPDecryptorCallback* aCallback,
|
||||||
|
GMPSessionType aSessionType);
|
||||||
|
|
||||||
~ClearKeySession();
|
~ClearKeySession();
|
||||||
|
|
||||||
const std::vector<KeyId>& GetKeyIds() { return mKeyIds; }
|
const std::vector<KeyId>& GetKeyIds() const { return mKeyIds; }
|
||||||
|
|
||||||
void Init(uint32_t aPromiseId,
|
void Init(uint32_t aPromiseId,
|
||||||
const uint8_t* aInitData, uint32_t aInitDataSize);
|
const uint8_t* aInitData, uint32_t aInitDataSize);
|
||||||
|
|
||||||
|
GMPSessionType Type() const;
|
||||||
|
|
||||||
|
void AddKeyId(const KeyId& aKeyId);
|
||||||
|
|
||||||
|
const std::string Id() const { return mSessionId; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mSessionId;
|
const std::string mSessionId;
|
||||||
std::vector<KeyId> mKeyIds;
|
std::vector<KeyId> mKeyIds;
|
||||||
|
|
||||||
GMPDecryptorCallback* mCallback;
|
GMPDecryptorCallback* mCallback;
|
||||||
|
const GMPSessionType mSessionType;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __ClearKeySession_h__
|
#endif // __ClearKeySession_h__
|
||||||
|
174
media/gmp-clearkey/0.1/ClearKeyStorage.cpp
Normal file
174
media/gmp-clearkey/0.1/ClearKeyStorage.cpp
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "ClearKeyStorage.h"
|
||||||
|
#include "ClearKeyUtils.h"
|
||||||
|
|
||||||
|
#include "gmp-task-utils.h"
|
||||||
|
|
||||||
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "mozilla/NullPtr.h"
|
||||||
|
#include "mozilla/ArrayUtils.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static GMPErr
|
||||||
|
RunOnMainThread(GMPTask* aTask)
|
||||||
|
{
|
||||||
|
return GetPlatform()->runonmainthread(aTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
class WriteRecordClient : public GMPRecordClient {
|
||||||
|
public:
|
||||||
|
GMPErr Init(GMPRecord* aRecord,
|
||||||
|
GMPTask* aOnSuccess,
|
||||||
|
GMPTask* aOnFailure,
|
||||||
|
const uint8_t* aData,
|
||||||
|
uint32_t aDataSize) {
|
||||||
|
mRecord = aRecord;
|
||||||
|
mOnSuccess = aOnSuccess;
|
||||||
|
mOnFailure = aOnFailure;
|
||||||
|
mData.insert(mData.end(), aData, aData + aDataSize);
|
||||||
|
return mRecord->Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
|
||||||
|
if (GMP_FAILED(aStatus) ||
|
||||||
|
GMP_FAILED(mRecord->Write(&mData.front(), mData.size()))) {
|
||||||
|
RunOnMainThread(mOnFailure);
|
||||||
|
mOnSuccess->Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void ReadComplete(GMPErr aStatus,
|
||||||
|
const uint8_t* aData,
|
||||||
|
uint32_t aDataSize) MOZ_OVERRIDE {
|
||||||
|
MOZ_ASSERT(false, "Should not reach here.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE {
|
||||||
|
// Note: Call Close() before running continuation, in case the
|
||||||
|
// continuation tries to open the same record; if we call Close()
|
||||||
|
// after running the continuation, the Close() call will arrive
|
||||||
|
// just after the Open() call succeeds, immediately closing the
|
||||||
|
// record we just opened.
|
||||||
|
mRecord->Close();
|
||||||
|
if (GMP_SUCCEEDED(aStatus)) {
|
||||||
|
RunOnMainThread(mOnSuccess);
|
||||||
|
mOnFailure->Destroy();
|
||||||
|
} else {
|
||||||
|
RunOnMainThread(mOnFailure);
|
||||||
|
mOnSuccess->Destroy();
|
||||||
|
}
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GMPRecord* mRecord;
|
||||||
|
GMPTask* mOnSuccess;
|
||||||
|
GMPTask* mOnFailure;
|
||||||
|
std::vector<uint8_t> mData;
|
||||||
|
};
|
||||||
|
|
||||||
|
GMPErr
|
||||||
|
OpenRecord(const char* aName,
|
||||||
|
uint32_t aNameLength,
|
||||||
|
GMPRecord** aOutRecord,
|
||||||
|
GMPRecordClient* aClient)
|
||||||
|
{
|
||||||
|
return GetPlatform()->createrecord(aName, aNameLength, aOutRecord, aClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StoreData(const std::string& aRecordName,
|
||||||
|
const std::vector<uint8_t>& aData,
|
||||||
|
GMPTask* aOnSuccess,
|
||||||
|
GMPTask* aOnFailure)
|
||||||
|
{
|
||||||
|
GMPRecord* record;
|
||||||
|
WriteRecordClient* client = new WriteRecordClient();
|
||||||
|
if (GMP_FAILED(OpenRecord(aRecordName.c_str(),
|
||||||
|
aRecordName.size(),
|
||||||
|
&record,
|
||||||
|
client)) ||
|
||||||
|
GMP_FAILED(client->Init(record,
|
||||||
|
aOnSuccess,
|
||||||
|
aOnFailure,
|
||||||
|
&aData.front(),
|
||||||
|
aData.size()))) {
|
||||||
|
RunOnMainThread(aOnFailure);
|
||||||
|
aOnSuccess->Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReadRecordClient : public GMPRecordClient {
|
||||||
|
public:
|
||||||
|
ReadRecordClient()
|
||||||
|
: mRecord(nullptr)
|
||||||
|
, mContinuation(nullptr)
|
||||||
|
{}
|
||||||
|
~ReadRecordClient() {
|
||||||
|
delete mContinuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
GMPErr Init(GMPRecord* aRecord,
|
||||||
|
ReadContinuation* aContinuation) {
|
||||||
|
mRecord = aRecord;
|
||||||
|
mContinuation = aContinuation;
|
||||||
|
return mRecord->Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
|
||||||
|
auto err = mRecord->Read();
|
||||||
|
if (GMP_FAILED(err)) {
|
||||||
|
mContinuation->ReadComplete(err, nullptr, 0);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void ReadComplete(GMPErr aStatus,
|
||||||
|
const uint8_t* aData,
|
||||||
|
uint32_t aDataSize) MOZ_OVERRIDE {
|
||||||
|
// Note: Call Close() before running continuation, in case the
|
||||||
|
// continuation tries to open the same record; if we call Close()
|
||||||
|
// after running the continuation, the Close() call will arrive
|
||||||
|
// just after the Open() call succeeds, immediately closing the
|
||||||
|
// record we just opened.
|
||||||
|
mRecord->Close();
|
||||||
|
mContinuation->ReadComplete(GMPNoErr, aData, aDataSize);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE {
|
||||||
|
MOZ_ASSERT(false, "Should not reach here.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GMPRecord* mRecord;
|
||||||
|
ReadContinuation* mContinuation;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
ReadData(const std::string& aRecordName,
|
||||||
|
ReadContinuation* aContinuation)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aContinuation);
|
||||||
|
GMPRecord* record;
|
||||||
|
ReadRecordClient* client = new ReadRecordClient();
|
||||||
|
auto err = OpenRecord(aRecordName.c_str(),
|
||||||
|
aRecordName.size(),
|
||||||
|
&record,
|
||||||
|
client);
|
||||||
|
if (GMP_FAILED(err) ||
|
||||||
|
GMP_FAILED(client->Init(record, aContinuation))) {
|
||||||
|
aContinuation->ReadComplete(err, nullptr, 0);
|
||||||
|
delete aContinuation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GMPErr
|
||||||
|
EnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc)
|
||||||
|
{
|
||||||
|
return GetPlatform()->getrecordenumerator(aRecvIteratorFunc, nullptr);
|
||||||
|
}
|
36
media/gmp-clearkey/0.1/ClearKeyStorage.h
Normal file
36
media/gmp-clearkey/0.1/ClearKeyStorage.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef __ClearKeyStorage_h__
|
||||||
|
#define __ClearKeyStorage_h__
|
||||||
|
|
||||||
|
#include "gmp-errors.h"
|
||||||
|
#include "gmp-platform.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class GMPTask;
|
||||||
|
|
||||||
|
// Responsible for ensuring that both aOnSuccess and aOnFailure are destroyed.
|
||||||
|
void StoreData(const std::string& aRecordName,
|
||||||
|
const std::vector<uint8_t>& aData,
|
||||||
|
GMPTask* aOnSuccess,
|
||||||
|
GMPTask* aOnFailure);
|
||||||
|
|
||||||
|
class ReadContinuation {
|
||||||
|
public:
|
||||||
|
virtual void ReadComplete(GMPErr aStatus,
|
||||||
|
const uint8_t* aData,
|
||||||
|
uint32_t aLength) = 0;
|
||||||
|
virtual ~ReadContinuation() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Deletes aContinuation after running it to report the result.
|
||||||
|
void ReadData(const std::string& aSessionId,
|
||||||
|
ReadContinuation* aContinuation);
|
||||||
|
|
||||||
|
GMPErr EnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc);
|
||||||
|
|
||||||
|
#endif // __ClearKeyStorage_h__
|
@ -177,7 +177,8 @@ ClearKeyUtils::ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize,
|
|||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs,
|
ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs,
|
||||||
string& aOutRequest)
|
string& aOutRequest,
|
||||||
|
GMPSessionType aSessionType)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aKeyIDs.size() && aOutRequest.empty());
|
MOZ_ASSERT(aKeyIDs.size() && aOutRequest.empty());
|
||||||
|
|
||||||
@ -195,9 +196,10 @@ ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs,
|
|||||||
aOutRequest.append("\"");
|
aOutRequest.append("\"");
|
||||||
}
|
}
|
||||||
aOutRequest.append("], \"type\":");
|
aOutRequest.append("], \"type\":");
|
||||||
// TODO implement "persistent" session type
|
|
||||||
aOutRequest.append("\"temporary\"");
|
aOutRequest.append("\"");
|
||||||
aOutRequest.append("}");
|
aOutRequest.append(SessionTypeToString(aSessionType));
|
||||||
|
aOutRequest.append("\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#define EXPECT_SYMBOL(CTX, X) do { \
|
#define EXPECT_SYMBOL(CTX, X) do { \
|
||||||
@ -509,7 +511,8 @@ ParseKeys(ParserContext& aCtx, vector<KeyIdPair>& aOutKeys)
|
|||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
|
ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
|
||||||
vector<KeyIdPair>& aOutKeys)
|
vector<KeyIdPair>& aOutKeys,
|
||||||
|
GMPSessionType aSessionType)
|
||||||
{
|
{
|
||||||
ParserContext ctx;
|
ParserContext ctx;
|
||||||
ctx.mIter = aKeyData;
|
ctx.mIter = aKeyData;
|
||||||
@ -531,8 +534,7 @@ ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
|
|||||||
// Consume type string.
|
// Consume type string.
|
||||||
string type;
|
string type;
|
||||||
if (!GetNextLabel(ctx, type)) return false;
|
if (!GetNextLabel(ctx, type)) return false;
|
||||||
// XXX todo support "persistent" session type
|
if (type != SessionTypeToString(aSessionType)) {
|
||||||
if (type != "temporary") {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -553,3 +555,32 @@ ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */ const char*
|
||||||
|
ClearKeyUtils::SessionTypeToString(GMPSessionType aSessionType)
|
||||||
|
{
|
||||||
|
switch (aSessionType) {
|
||||||
|
case kGMPTemporySession: return "temporary";
|
||||||
|
case kGMPPersistentSession: return "persistent";
|
||||||
|
default: {
|
||||||
|
MOZ_ASSERT(false, "Should not reach here.");
|
||||||
|
return "invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ bool
|
||||||
|
ClearKeyUtils::IsValidSessionId(const char* aBuff, uint32_t aLength)
|
||||||
|
{
|
||||||
|
if (aLength > 10) {
|
||||||
|
// 10 is the max number of characters in UINT32_MAX when
|
||||||
|
// represented as a string; ClearKey session ids are integers.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < aLength; i++) {
|
||||||
|
if (!isdigit(aBuff[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "gmp-decryption.h"
|
||||||
|
|
||||||
#define CLEARKEY_KEY_LEN ((size_t)16)
|
#define CLEARKEY_KEY_LEN ((size_t)16)
|
||||||
|
|
||||||
@ -44,10 +45,22 @@ public:
|
|||||||
std::vector<Key>& aOutKeys);
|
std::vector<Key>& aOutKeys);
|
||||||
|
|
||||||
static void MakeKeyRequest(const std::vector<KeyId>& aKeyIds,
|
static void MakeKeyRequest(const std::vector<KeyId>& aKeyIds,
|
||||||
std::string& aOutRequest);
|
std::string& aOutRequest,
|
||||||
|
GMPSessionType aSessionType);
|
||||||
|
|
||||||
static bool ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
|
static bool ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
|
||||||
std::vector<KeyIdPair>& aOutKeys);
|
std::vector<KeyIdPair>& aOutKeys,
|
||||||
|
GMPSessionType aSessionType);
|
||||||
|
static const char* SessionTypeToString(GMPSessionType aSessionType);
|
||||||
|
|
||||||
|
static bool IsValidSessionId(const char* aBuff, uint32_t aLength);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class Container, class Element>
|
||||||
|
inline bool
|
||||||
|
Contains(const Container& aContainer, const Element& aElement)
|
||||||
|
{
|
||||||
|
return aContainer.find(aElement) != aContainer.end();
|
||||||
|
}
|
||||||
|
|
||||||
#endif // __ClearKeyUtils_h__
|
#endif // __ClearKeyUtils_h__
|
||||||
|
34
media/gmp-clearkey/0.1/RefCounted.h
Normal file
34
media/gmp-clearkey/0.1/RefCounted.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef __RefCount_h__
|
||||||
|
#define __RefCount_h__
|
||||||
|
|
||||||
|
// Note: Not thread safe!
|
||||||
|
class RefCounted {
|
||||||
|
public:
|
||||||
|
void AddRef() {
|
||||||
|
++mRefCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Release() {
|
||||||
|
if (mRefCount == 1) {
|
||||||
|
delete this;
|
||||||
|
} else {
|
||||||
|
--mRefCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RefCounted()
|
||||||
|
: mRefCount(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~RefCounted()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
uint32_t mRefCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __RefCount_h__
|
1898
media/gmp-clearkey/0.1/gmp-task-utils-generated.h
Normal file
1898
media/gmp-clearkey/0.1/gmp-task-utils-generated.h
Normal file
File diff suppressed because it is too large
Load Diff
37
media/gmp-clearkey/0.1/gmp-task-utils.h
Normal file
37
media/gmp-clearkey/0.1/gmp-task-utils.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// Original author: ekr@rtfm.com
|
||||||
|
|
||||||
|
#ifndef gmp_task_utils_h_
|
||||||
|
#define gmp_task_utils_h_
|
||||||
|
|
||||||
|
#include "gmp-platform.h"
|
||||||
|
|
||||||
|
class gmp_task_args_base : public GMPTask {
|
||||||
|
public:
|
||||||
|
virtual void Destroy() { delete this; }
|
||||||
|
virtual void Run() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The generated file contains four major function templates
|
||||||
|
// (in variants for arbitrary numbers of arguments up to 10,
|
||||||
|
// which is why it is machine generated). The four templates
|
||||||
|
// are:
|
||||||
|
//
|
||||||
|
// WrapTask(o, m, ...) -- wraps a member function m of an object ptr o
|
||||||
|
// WrapTaskRet(o, m, ..., r) -- wraps a member function m of an object ptr o
|
||||||
|
// the function returns something that can
|
||||||
|
// be assigned to *r
|
||||||
|
// WrapTaskNM(f, ...) -- wraps a function f
|
||||||
|
// WrapTaskNMRet(f, ..., r) -- wraps a function f that returns something
|
||||||
|
// that can be assigned to *r
|
||||||
|
//
|
||||||
|
// All of these template functions return a GMPTask* which can be passed
|
||||||
|
// to DispatchXX().
|
||||||
|
#include "gmp-task-utils-generated.h"
|
||||||
|
|
||||||
|
#endif // gmp_task_utils_h_
|
@ -10,7 +10,9 @@ FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
|
|||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
'ClearKeyDecryptionManager.cpp',
|
'ClearKeyDecryptionManager.cpp',
|
||||||
|
'ClearKeyPersistence.cpp',
|
||||||
'ClearKeySession.cpp',
|
'ClearKeySession.cpp',
|
||||||
|
'ClearKeyStorage.cpp',
|
||||||
'ClearKeyUtils.cpp',
|
'ClearKeyUtils.cpp',
|
||||||
'gmp-clearkey.cpp',
|
'gmp-clearkey.cpp',
|
||||||
'openaes/oaes_lib.c',
|
'openaes/oaes_lib.c',
|
||||||
|
Loading…
Reference in New Issue
Block a user