2014-07-11 07:35:56 +04:00
|
|
|
/*
|
|
|
|
* Copyright 2013, Mozilla Foundation and contributors
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef GMP_DECRYPTION_h_
|
|
|
|
#define GMP_DECRYPTION_h_
|
|
|
|
|
|
|
|
#include "gmp-platform.h"
|
|
|
|
|
2014-07-24 01:35:02 +04:00
|
|
|
class GMPEncryptedBufferMetadata {
|
2014-07-11 07:35:56 +04:00
|
|
|
public:
|
|
|
|
// Key ID to identify the decryption key.
|
|
|
|
virtual const uint8_t* KeyId() const = 0;
|
|
|
|
|
|
|
|
// Size (in bytes) of |KeyId()|.
|
|
|
|
virtual uint32_t KeyIdSize() const = 0;
|
|
|
|
|
|
|
|
// Initialization vector.
|
|
|
|
virtual const uint8_t* IV() const = 0;
|
|
|
|
|
|
|
|
// Size (in bytes) of |IV|.
|
|
|
|
virtual uint32_t IVSize() const = 0;
|
|
|
|
|
2014-07-24 01:35:02 +04:00
|
|
|
// Number of entries returned by ClearBytes() and CipherBytes().
|
2014-07-11 07:35:56 +04:00
|
|
|
virtual uint32_t NumSubsamples() const = 0;
|
|
|
|
|
2014-07-24 01:35:02 +04:00
|
|
|
virtual const uint16_t* ClearBytes() const = 0;
|
2014-07-11 07:35:56 +04:00
|
|
|
|
|
|
|
virtual const uint32_t* CipherBytes() const = 0;
|
2014-11-14 11:36:20 +03:00
|
|
|
|
|
|
|
virtual ~GMPEncryptedBufferMetadata() {}
|
2014-07-11 07:35:56 +04:00
|
|
|
};
|
|
|
|
|
2014-07-24 01:35:02 +04:00
|
|
|
class GMPBuffer {
|
|
|
|
public:
|
|
|
|
virtual uint32_t Id() const = 0;
|
|
|
|
virtual uint8_t* Data() = 0;
|
|
|
|
virtual uint32_t Size() const = 0;
|
|
|
|
virtual void Resize(uint32_t aSize) = 0;
|
|
|
|
virtual ~GMPBuffer() {}
|
|
|
|
};
|
|
|
|
|
2014-07-11 07:35:56 +04:00
|
|
|
// These match to the DOMException codes as per:
|
|
|
|
// http://www.w3.org/TR/dom/#domexception
|
|
|
|
enum GMPDOMException {
|
|
|
|
kGMPNoModificationAllowedError = 7,
|
|
|
|
kGMPNotFoundError = 8,
|
|
|
|
kGMPNotSupportedError = 9,
|
|
|
|
kGMPInvalidStateError = 11,
|
|
|
|
kGMPSyntaxError = 12,
|
|
|
|
kGMPInvalidModificationError = 13,
|
|
|
|
kGMPInvalidAccessError = 15,
|
|
|
|
kGMPSecurityError = 18,
|
|
|
|
kGMPAbortError = 20,
|
|
|
|
kGMPQuotaExceededError = 22,
|
|
|
|
kGMPTimeoutError = 23
|
|
|
|
};
|
|
|
|
|
2015-01-15 01:25:47 +03:00
|
|
|
enum GMPSessionMessageType {
|
|
|
|
kGMPLicenseRequest = 0,
|
|
|
|
kGMPLicenseRenewal = 1,
|
|
|
|
kGMPLicenseRelease = 2,
|
2015-01-26 22:08:00 +03:00
|
|
|
kGMPIndividualizationRequest = 3,
|
|
|
|
kGMPMessageInvalid = 4 // Must always be last.
|
2015-01-15 01:25:47 +03:00
|
|
|
};
|
|
|
|
|
2014-07-11 07:35:56 +04:00
|
|
|
// Time in milliseconds, as offset from epoch, 1 Jan 1970.
|
|
|
|
typedef int64_t GMPTimestamp;
|
|
|
|
|
2014-07-24 01:35:02 +04:00
|
|
|
// Capability definitions. The capabilities of the EME GMP are reported
|
|
|
|
// to Gecko by calling the GMPDecryptorCallback::SetCapabilities()
|
|
|
|
// callback and specifying the logical OR of the GMP_EME_CAP_* flags below.
|
|
|
|
//
|
|
|
|
// Note the DECRYPT and the DECRYPT_AND_DECODE are mutually exclusive;
|
|
|
|
// only one mode should be reported for each stream type, but different
|
|
|
|
// modes can be reported for different stream types.
|
|
|
|
//
|
|
|
|
// Note: Gecko does not currently support the caps changing at runtime.
|
|
|
|
// Set them once per plugin initialization, during the startup of
|
|
|
|
// the GMPdecryptor.
|
|
|
|
|
|
|
|
// Capability; CDM can decrypt encrypted buffers and return still
|
|
|
|
// compressed buffers back to Gecko for decompression there.
|
|
|
|
#define GMP_EME_CAP_DECRYPT_AUDIO (uint64_t(1) << 0)
|
|
|
|
#define GMP_EME_CAP_DECRYPT_VIDEO (uint64_t(1) << 1)
|
|
|
|
|
|
|
|
// Capability; CDM can decrypt and then decode encrypted buffers,
|
|
|
|
// and return decompressed samples to Gecko for playback.
|
|
|
|
#define GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO (uint64_t(1) << 2)
|
|
|
|
#define GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO (uint64_t(1) << 3)
|
|
|
|
|
2014-09-24 02:04:48 +04:00
|
|
|
// Callbacks to be called from the CDM. Threadsafe.
|
2014-07-11 07:35:56 +04:00
|
|
|
class GMPDecryptorCallback {
|
|
|
|
public:
|
2015-01-09 04:30:07 +03:00
|
|
|
|
|
|
|
// The GMPDecryptor should call this in response to a call to
|
|
|
|
// GMPDecryptor::CreateSession(). The GMP host calls CreateSession() when
|
|
|
|
// MediaKeySession.generateRequest() is called by JavaScript.
|
|
|
|
// After CreateSession() is called, the GMPDecryptor should call
|
|
|
|
// GMPDecryptorCallback::SetSessionId() to set the sessionId exposed to
|
|
|
|
// JavaScript on the MediaKeySession on which the generateRequest() was
|
|
|
|
// called. SetSessionId() must be called before
|
|
|
|
// GMPDecryptorCallback::SessionMessage() will work.
|
2014-07-11 07:35:56 +04:00
|
|
|
// aSessionId must be null terminated.
|
2015-01-09 04:30:07 +03:00
|
|
|
// Note: pass the aCreateSessionToken from the CreateSession() call,
|
|
|
|
// and then once the session has sent any messages required for the
|
|
|
|
// license request to be sent, then resolve the aPromiseId that was passed
|
|
|
|
// to GMPDecryptor::CreateSession().
|
|
|
|
// Note: GMPDecryptor::LoadSession() does *not* need to call SetSessionId()
|
|
|
|
// for GMPDecryptorCallback::SessionMessage() to work.
|
|
|
|
virtual void SetSessionId(uint32_t aCreateSessionToken,
|
|
|
|
const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength) = 0;
|
2014-07-11 07:35:56 +04:00
|
|
|
|
2014-09-24 02:04:49 +04:00
|
|
|
// Resolves a promise for a session loaded.
|
|
|
|
// Resolves to false if we don't have any session data stored for the given
|
|
|
|
// session ID.
|
|
|
|
// Must be called before SessionMessage().
|
|
|
|
virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
|
|
|
|
bool aSuccess) = 0;
|
|
|
|
|
2014-07-11 07:35:56 +04:00
|
|
|
// Called to resolve a specified promise with "undefined".
|
2014-07-24 01:35:02 +04:00
|
|
|
virtual void ResolvePromise(uint32_t aPromiseId) = 0;
|
2014-07-11 07:35:56 +04:00
|
|
|
|
|
|
|
// Called to reject a promise with a DOMException.
|
|
|
|
// aMessage is logged to the WebConsole.
|
|
|
|
// aMessage is optional, but if present must be null terminated.
|
2014-07-24 01:35:02 +04:00
|
|
|
virtual void RejectPromise(uint32_t aPromiseId,
|
|
|
|
GMPDOMException aException,
|
|
|
|
const char* aMessage,
|
|
|
|
uint32_t aMessageLength) = 0;
|
2014-07-11 07:35:56 +04:00
|
|
|
|
2015-01-09 04:30:07 +03:00
|
|
|
// Called by the CDM when it has a message for a session.
|
2014-07-11 07:35:56 +04:00
|
|
|
// Length parameters should not include null termination.
|
|
|
|
// aSessionId must be null terminated.
|
2014-07-24 01:35:02 +04:00
|
|
|
virtual void SessionMessage(const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength,
|
2015-01-15 01:25:47 +03:00
|
|
|
GMPSessionMessageType aMessageType,
|
2014-07-24 01:35:02 +04:00
|
|
|
const uint8_t* aMessage,
|
2015-01-15 01:25:47 +03:00
|
|
|
uint32_t aMessageLength) = 0;
|
2014-07-11 07:35:56 +04:00
|
|
|
|
|
|
|
// aSessionId must be null terminated.
|
2014-07-24 01:35:02 +04:00
|
|
|
virtual void ExpirationChange(const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength,
|
|
|
|
GMPTimestamp aExpiryTime) = 0;
|
2014-07-11 07:35:56 +04:00
|
|
|
|
|
|
|
// Called by the GMP when a session is closed. All file IO
|
|
|
|
// that a session requires should be complete before calling this.
|
|
|
|
// aSessionId must be null terminated.
|
2014-07-24 01:35:02 +04:00
|
|
|
virtual void SessionClosed(const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength) = 0;
|
2014-07-11 07:35:56 +04:00
|
|
|
|
|
|
|
// Called by the GMP when an error occurs in a session.
|
|
|
|
// aSessionId must be null terminated.
|
|
|
|
// aMessage is logged to the WebConsole.
|
|
|
|
// aMessage is optional, but if present must be null terminated.
|
2014-07-24 01:35:02 +04:00
|
|
|
virtual void SessionError(const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength,
|
|
|
|
GMPDOMException aException,
|
|
|
|
uint32_t aSystemCode,
|
|
|
|
const char* aMessage,
|
|
|
|
uint32_t aMessageLength) = 0;
|
|
|
|
|
|
|
|
// Marks a key as usable. Gecko will not call into the CDM to decrypt
|
|
|
|
// or decode content encrypted with a key unless the CDM has marked it
|
|
|
|
// usable first. So a CDM *MUST* mark its usable keys as usable!
|
|
|
|
virtual void KeyIdUsable(const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength,
|
|
|
|
const uint8_t* aKeyId,
|
|
|
|
uint32_t aKeyIdLength) = 0;
|
2014-07-11 07:35:56 +04:00
|
|
|
|
|
|
|
// Marks a key as no longer usable.
|
|
|
|
// Note: Keys are assumed to be not usable when a session is closed or removed.
|
2014-07-24 01:35:02 +04:00
|
|
|
virtual void KeyIdNotUsable(const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength,
|
|
|
|
const uint8_t* aKeyId,
|
|
|
|
uint32_t aKeyIdLength) = 0;
|
|
|
|
|
|
|
|
// The CDM must report its capabilites of this CDM. aCaps should be a
|
|
|
|
// logical OR of the GMP_EME_CAP_* flags. The CDM *MUST* call this
|
|
|
|
// function and report whether it can decrypt and/or decode. Without
|
|
|
|
// this, Gecko does not know how to use the CDM and will not send
|
|
|
|
// samples to the CDM to decrypt or decrypt-and-decode mode. Note a
|
|
|
|
// CDM cannot change modes once playback has begun.
|
|
|
|
virtual void SetCapabilities(uint64_t aCaps) = 0;
|
|
|
|
|
|
|
|
// Returns decrypted buffer to Gecko, or reports failure.
|
|
|
|
virtual void Decrypted(GMPBuffer* aBuffer, GMPErr aResult) = 0;
|
2014-11-14 11:36:20 +03:00
|
|
|
|
|
|
|
virtual ~GMPDecryptorCallback() {}
|
2014-07-11 07:35:56 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
// Host interface, passed to GetAPIFunc(), with "decrypt".
|
|
|
|
class GMPDecryptorHost {
|
|
|
|
public:
|
|
|
|
virtual void GetSandboxVoucher(const uint8_t** aVoucher,
|
2014-11-14 11:39:39 +03:00
|
|
|
uint32_t* aVoucherLength) = 0;
|
2014-07-11 07:35:56 +04:00
|
|
|
|
|
|
|
virtual void GetPluginVoucher(const uint8_t** aVoucher,
|
2014-11-14 11:39:39 +03:00
|
|
|
uint32_t* aVoucherLength) = 0;
|
2014-11-14 11:36:20 +03:00
|
|
|
|
|
|
|
virtual ~GMPDecryptorHost() {}
|
2014-07-11 07:35:56 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
enum GMPSessionType {
|
|
|
|
kGMPTemporySession = 0,
|
2014-11-03 03:53:01 +03:00
|
|
|
kGMPPersistentSession = 1,
|
|
|
|
kGMPSessionInvalid = 2 // Must always be last.
|
2014-07-11 07:35:56 +04:00
|
|
|
};
|
|
|
|
|
2015-01-26 22:08:00 +03:00
|
|
|
#define GMP_API_DECRYPTOR "eme-decrypt-v4"
|
2014-12-09 23:35:26 +03:00
|
|
|
|
2014-07-11 07:35:56 +04:00
|
|
|
// API exposed by plugin library to manage decryption sessions.
|
|
|
|
// When the Host requests this by calling GMPGetAPIFunc().
|
|
|
|
//
|
2014-12-09 23:35:26 +03:00
|
|
|
// API name macro: GMP_API_DECRYPTOR
|
2014-07-11 07:35:56 +04:00
|
|
|
// Host API: GMPDecryptorHost
|
|
|
|
class GMPDecryptor {
|
|
|
|
public:
|
|
|
|
|
|
|
|
// Sets the callback to use with the decryptor to return results
|
|
|
|
// to Gecko.
|
2014-07-24 01:35:02 +04:00
|
|
|
//
|
|
|
|
// The CDM must also call GMPDecryptorCallback::SetCapabilities()
|
|
|
|
// exactly once during start up, to inform Gecko whether to use the CDM
|
|
|
|
// in decrypt or decrypt-and-decode mode.
|
2015-01-09 04:30:07 +03:00
|
|
|
//
|
|
|
|
// Note: GMPDecryptorCallback::SetCapabilities() must be called before
|
|
|
|
// Gecko will send any samples for decryption to the GMP.
|
|
|
|
virtual void Init(GMPDecryptorCallback* aCallback) = 0;
|
|
|
|
|
|
|
|
// Initiates the creation of a session given |aType| and |aInitData|, and
|
|
|
|
// the generation of a license request message.
|
|
|
|
//
|
|
|
|
// This corresponds to a MediaKeySession.generateRequest() call in JS.
|
|
|
|
//
|
|
|
|
// The GMPDecryptor must do the following, in order, upon this method
|
|
|
|
// being called:
|
|
|
|
//
|
|
|
|
// 1. Generate a sessionId to expose to JS, and call
|
|
|
|
// GMPDecryptorCallback::SetSessionId(aCreateSessionToken, sessionId...)
|
|
|
|
// with the sessionId to be exposed to JS/EME on the MediaKeySession
|
|
|
|
// object on which generateRequest() was called, and then
|
|
|
|
// 2. send any messages to JS/EME required to generate a license request
|
|
|
|
// given the supplied initData, and then
|
|
|
|
// 3. generate a license request message, and send it to JS/EME, and then
|
|
|
|
// 4. call GMPDecryptorCallback::ResolvePromise().
|
|
|
|
//
|
|
|
|
// Note: GMPDecryptorCallback::SetSessionId(aCreateSessionToken, sessionId, ...)
|
|
|
|
// *must* be called before GMPDecryptorCallback::SendMessage(sessionId, ...)
|
|
|
|
// will work.
|
|
|
|
//
|
|
|
|
// If generating the request fails, reject aPromiseId by calling
|
|
|
|
// GMPDecryptorCallback::RejectPromise().
|
|
|
|
virtual void CreateSession(uint32_t aCreateSessionToken,
|
|
|
|
uint32_t aPromiseId,
|
2014-07-11 07:35:56 +04:00
|
|
|
const char* aInitDataType,
|
|
|
|
uint32_t aInitDataTypeSize,
|
|
|
|
const uint8_t* aInitData,
|
|
|
|
uint32_t aInitDataSize,
|
|
|
|
GMPSessionType aSessionType) = 0;
|
|
|
|
|
|
|
|
// Loads a previously loaded persistent session.
|
2015-01-09 04:30:07 +03:00
|
|
|
//
|
|
|
|
// This corresponds to a MediaKeySession.load() call in JS.
|
|
|
|
//
|
|
|
|
// The GMPDecryptor must do the following, in order, upon this method
|
|
|
|
// being called:
|
|
|
|
//
|
|
|
|
// 1. Send any messages to JS/EME, or read from storage, whatever is
|
|
|
|
// required to load the session, and then
|
|
|
|
// 2. if there is no session with the given sessionId loadable, call
|
|
|
|
// ResolveLoadSessionPromise(aPromiseId, false), otherwise
|
|
|
|
// 2. mark the session's keys as usable, and then
|
|
|
|
// 3. update the session's expiration, and then
|
|
|
|
// 4. call GMPDecryptorCallback::ResolveLoadSessionPromise(aPromiseId, true).
|
|
|
|
//
|
|
|
|
// If loading the session fails due to error, reject aPromiseId by calling
|
|
|
|
// GMPDecryptorCallback::RejectPromise().
|
2014-07-11 07:35:56 +04:00
|
|
|
virtual void LoadSession(uint32_t aPromiseId,
|
|
|
|
const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength) = 0;
|
|
|
|
|
|
|
|
// Updates the session with |aResponse|.
|
2015-01-09 04:30:07 +03:00
|
|
|
// This corresponds to a MediaKeySession.update() call in JS.
|
2014-07-11 07:35:56 +04:00
|
|
|
virtual void UpdateSession(uint32_t aPromiseId,
|
|
|
|
const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength,
|
|
|
|
const uint8_t* aResponse,
|
|
|
|
uint32_t aResponseSize) = 0;
|
|
|
|
|
|
|
|
// Releases the resources (keys) for the specified session.
|
2015-01-09 04:30:07 +03:00
|
|
|
// This corresponds to a MediaKeySession.close() call in JS.
|
2014-07-11 07:35:56 +04:00
|
|
|
virtual void CloseSession(uint32_t aPromiseId,
|
|
|
|
const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength) = 0;
|
|
|
|
|
|
|
|
// Removes the resources (keys) for the specified session.
|
2015-01-09 04:30:07 +03:00
|
|
|
// This corresponds to a MediaKeySession.remove() call in JS.
|
2014-07-11 07:35:56 +04:00
|
|
|
virtual void RemoveSession(uint32_t aPromiseId,
|
|
|
|
const char* aSessionId,
|
|
|
|
uint32_t aSessionIdLength) = 0;
|
|
|
|
|
|
|
|
// Resolve/reject promise on completion.
|
2015-01-09 04:30:07 +03:00
|
|
|
// This corresponds to a MediaKeySession.setServerCertificate() call in JS.
|
2014-07-11 07:35:56 +04:00
|
|
|
virtual void SetServerCertificate(uint32_t aPromiseId,
|
|
|
|
const uint8_t* aServerCert,
|
|
|
|
uint32_t aServerCertSize) = 0;
|
2014-07-24 01:35:02 +04:00
|
|
|
|
|
|
|
// Asynchronously decrypts aBuffer in place. When the decryption is
|
|
|
|
// complete, GMPDecryptor should write the decrypted data back into the
|
|
|
|
// same GMPBuffer object and return it to Gecko by calling Decrypted(),
|
|
|
|
// with the GMPNoErr successcode. If decryption fails, call Decrypted()
|
|
|
|
// with a failure code, and an error event will fire on the media element.
|
2014-08-05 11:56:04 +04:00
|
|
|
// Note: When Decrypted() is called and aBuffer is passed back, aBuffer
|
|
|
|
// is deleted. Don't forget to call Decrypted(), as otherwise aBuffer's
|
|
|
|
// memory will leak!
|
2014-07-24 01:35:02 +04:00
|
|
|
virtual void Decrypt(GMPBuffer* aBuffer,
|
|
|
|
GMPEncryptedBufferMetadata* aMetadata) = 0;
|
|
|
|
|
2014-07-24 01:35:02 +04:00
|
|
|
// Called when the decryption operations are complete.
|
|
|
|
// Do not call the GMPDecryptorCallback's functions after this is called.
|
|
|
|
virtual void DecryptingComplete() = 0;
|
|
|
|
|
2014-11-14 11:36:20 +03:00
|
|
|
virtual ~GMPDecryptor() {}
|
2014-07-11 07:35:56 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif // GMP_DECRYPTION_h_
|