Abstract

This proposal extends HTMLMediaElement [HTML5] providing APIs to control playback of protected content.

The API supports use cases ranging from simple clear key decryption to high value video (given an appropriate user agent implementation). License/key exchange is controlled by the application, facilitating the development of robust playback applications supporting a range of content decryption and protection technologies.

This specification does not define a content protection or Digital Rights Management system. Rather, it defines a common API that may be used to discover, select and interact with such systems as well as with simpler content encryption systems. Implementation of Digital Rights Management is not required for compliance with this specification: only the Clear Key system is required to be implemented as a common baseline.

The common API supports a simple set of content encryption capabilities, leaving application functions such as authentication and authorization to page authors. This is achieved by requiring content protection system-specific messaging to be mediated by the page rather than assuming out-of-band communication between the encryption system and a license or other server.

Status of This Document

This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.

The working groups maintains a list of all bug reports that the editors have not yet tried to address. This draft highlights some of the pending issues that are still to be discussed in the working group. No decision has been taken on the outcome of these issues including whether they are valid.

Implementors should be aware that this specification is not stable. Implementors who are not taking part in the discussions are likely to find the specification changing out from under them in incompatible ways. Vendors interested in implementing this specification before it eventually reaches the Candidate Recommendation stage should join the mailing list mentioned below and take part in the discussions.

Note: It is an open issue whether and how the specification should do more to encourage/ensure CDM-level interoperability. See Bug 20944.

Note: This specification contains sections for describing security and privacy considerations. These sections are not final and review is welcome.

This document was published by the HTML Working Group as an Editor's Draft. If you wish to make comments regarding this document, please send them to public-html-media@w3.org (subscribe, archives). All comments are welcome.

Publication as an Editor's Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 1 August 2014 W3C Process Document.

Table of Contents

1. Introduction

This section is non-normative.

This proposal allows JavaScript to select content protection mechanisms, control license/key exchange, and implement custom license management algorithms. It supports a wide range of use cases without requiring client-side modifications in each user agent for each use case. This also enables content providers to develop a single application solution for all devices. A generic stack implemented using the proposed APIs is shown below. This diagram shows an example flow: other combinations of API calls and events are possible.

A generic stack implemented using the proposed APIs

1.1 Definitions

Content Decryption Module (CDM)

The Content Decryption Module (CDM) is a generic term for the client component that provides the functionality, including decryption, for one or more Key Systems.

Note

Implementations may or may not separate the implementations of CDMs or treat them as separate from the user agent. This is transparent to the API and application.

A user agent MAY support one or more CDMs.

Key System

A Key System is a generic term for a decryption mechanism and/or content protection provider. Key System strings provide unique identification of a Key System. They are used by the user agent to select a CDM and identify the source of a key-related event. The Common Key Systems are supported by all user agents. User agents MAY also provide additional CDMs with corresponding Key System strings.

A Key System string is always a reverse domain name. Key System strings are compared using case-sensitive matching. It is RECOMMENDED that CDMs use simple lower-case ASCII key system strings.

Note

For example, "com.example.somesystem".

Note

Within a given system ("somesystem" in the example), subsystems may be defined as determined by the key system provider. For example, "com.example.somesystem.1" and "com.example.somesystem.1_5". Key System providers should keep in mind that these will be used for comparison and discovery, so they should be easy to compare and the structure should remain reasonably simple.

Key Session

A Key Session, or simply Session, provides a context for message exchange with the CDM as a result of which key(s) are made available to the CDM. Sessions are embodied as MediaKeySession objects. Each Key session is associated with a single instance of Initialization Data provided in the generateRequest() call.

Each Key Session is associated with a single MediaKeys object, and only media element(s) associated with that object may access key(s) associated with the session. Other MediaKeys objects, CDM instances, and media elements MUST NOT access the key session or use its key(s). Key sessions and the keys they contain are no longer usable by the CDM for decryption when the session is closed, including when the MediaKeySession object is destroyed.

Session ID

A Session ID is a unique string identifier generated by the CDM that can be used by the application to identify MediaKeySession objects.

A new Session ID is generated each time the user agent and CDM successfully create a new session.

Each Session ID SHALL be unique within the browsing context in which it was created. Session IDs for "persistent" sessions MUST be unique within the origin over time, including across browsing sessions.

Note

The underlying content protection protocol does not necessarily need to support Session IDs.

Key

Unless otherwise stated, key refers to a decryption key that can be used to decrypt blocks within media data. Each such key is uniquely identified by a key ID. A key is associated with the session used to provide it to the CDM. (The same key may be present in multiple sessions.) Such keys MUST only be provided to the CDM via an update() call. (They may later be loaded by load() as part of the stored session data.)

A key is considered usable if the CDM is certain the key is currently usable for decryption.

Note

For example, a key is not usable if its license has expired.

Key ID

A key is associated with a key ID, which uniquely identifies a key. The container specifies the ID of the key that can decrypt a block or set of blocks within the media data. Initialization Data MAY contain key ID(s) to identify the keys that are needed to decrypt the media data. However, there is no requirement that Initialization Data contain any or all key IDs used in the media data or media resource. Licenses provided to the CDM associate each key with a key ID so the CDM can select the appropriate key when decrypting an encrypted block of media data.

License

A license is key system-specific state information that includes one or more key(s) - each associated with a key ID - and potentially other information about key usage.

Initialization Data
Note

Key Systems usually require a block of initialization data containing information about the stream to be decrypted before they can construct a license request message. This block could be a simple key or content ID or a more complex structure containing such information. It should always allow unique identification of the key(s) needed to decrypt the content. This initialization information MAY be obtained in some application-specific way or provided with the media data.

Initialization Data is a generic term for container-specific data that is used by a CDM to generate a license request. Initialization data found with the media data is provided to the application in the initData attribute of the encrypted event.

The format of the initialization data depends upon the type of container, and containers MAY support more than one format of initialization data. The initialization data type is a string that indicates what format the initialization data is provided in. Initialization data type strings are always matched case-sensitively. It is RECOMMENDED that initialization data type strings are lower-case ASCII strings.

The Encrypted Media Extensions Stream Format and Initialization Data Format Registry [EME-REGISTRY] provides the mapping from initialization data type string to the specification for each format.

Cross Origin Limitations

During playback, embedded media data is exposed to script in the embedding origin. In order for the API to provide Initialization Data in the encrypted event, media data MUST be CORS-same-origin with the embedding page. If media data is cross-origin with the embedding document, authors SHOULD use the crossorigin attribute on the HTMLMediaElement and CORS headers on the media data response to make it CORS-same-origin.

Mixed Content Limitations

During playback, embedded media data is exposed to script in the embedding origin. In order for the API to provide Initialization Data in the encrypted event, media data MUST NOT be Mixed Content [MIXED-CONTENT].

2. MediaKeys Object

The MediaKeys object represents a set of keys that an associated HTMLMediaElement can use for decryption of media data during playback. It also represents a CDM instance.

For methods that return a promise, all errors are reported asynchronously by rejecting the returned Promise. This includes WebIDL type mapping errors.

The steps of an algorithm are always aborted when resolving or rejecting a promise.

Issue 1

Bug 25923 - Feature detection, including isTypeSupported(), and MediaKeys creation may change significantly.

enum IsTypeSupportedResult {
    "/* empty string */",
    "maybe",
    "probably"
};
Enumeration description
/* empty string */ The combination is not supported.
maybe The combination may be supported.
probably The combination is likely supported.
enum SessionType {
    "temporary",
    "persistent"
};
Enumeration description
temporary A session for which a record of or data related to the session MUST NOT be persisted. The application need not worry about managing such storage.
persistent A session for which a record of or data related to the session MAY be persisted. The application is responsible for managing any such storage that may be generated by the CDM. See Session Storage and Persistence.
interface MediaKeys {
    readonly    attribute DOMString keySystem;
    MediaKeySession              createSession (optional SessionType sessionType = "temporary");
    Promise<void>                setServerCertificate ((ArrayBuffer or ArrayBufferView) serverCertificate);
    static Promise<MediaKeys>    create (DOMString keySystem);
    static IsTypeSupportedResult isTypeSupported (DOMstring keySystem, optional DOMString initDataType, optional DOMString contentType, optional DOMString capability);
};

2.1 Attributes

keySystem of type DOMString, readonly
Identifies the Key System being used.

2.2 Methods

create, static

Creates a new MediaKeys object for keySystem.

ParameterTypeNullableOptionalDescription
keySystemDOMString
Return type: Promise<MediaKeys>

When this method is invoked, the user agent must run the following steps:

  1. If keySystem is an empty string, return a promise rejected with a new DOMException whose name is InvalidAccessError.

  2. If keySystem is not one of the Key Systems supported by the user agent, return a promise rejected with a new DOMException whose name is NotSupportedError. String comparison is case-sensitive.

  3. If keySystem is not supported or not allowed in the origin of the calling context's Document, return a promise rejected with a new DOMException whose name is NotSupportedError.

  4. Let promise be a new promise.

  5. Run the following steps asynchronously:

    1. Let cdm be the CDM corresponding to keySystem.

    2. Load and initialize the cdm if necessary.

    3. If cdm fails to load or initialize, reject promise with a new DOMException whose name is the appropriate error name.

    4. Let media keys be a new MediaKeys object, and initialize it as follows:

      1. Set the keySystem attribute to keySystem.

    5. Resolve promise with media keys.

  6. Return promise.

createSession

Returns a new MediaKeySession object.

ParameterTypeNullableOptionalDescription
sessionTypeSessionType = "temporary"
Return type: MediaKeySession

When this method is invoked, the user agent must run the following steps:

  1. If sessionType is not supported by the CDM corresponding to the keySystem attribute, throw a new DOMException whose name is NotSupportedError.

  2. Let session be a new MediaKeySession object, and initialize it as follows:

    1. Let the sessionId attribute be the empty string.

    2. Let the expiration attribute be NaN.

    3. Let the closed attribute be a new promise.

    4. Let the session type be sessionType.

    5. Let uninitialized be true.

    6. Let callable be false.

  3. Return session.

isTypeSupported, static

Returns whether keySystem is supported with the initDataType, container and codec(s) specified by contentType, and capability.

The following list shows some examples.

    Returns whether the Some System Key System may be supported. Specific containers and codecs may or may not be supported with Some System.
    MediaKeys.isTypeSupported("com.example.somesystem")
    Returns whether the Some System Key System may be supported and is capable of parsing the Initialization Data format "keyids". Specific containers and codecs may or may not be supported with Some System.
    MediaKeys.isTypeSupported("com.example.somesystem", "keyids")
    Returns whether the Some System Key System may be supported and is capable of parsing the Initialization Data format initDataType, the user agent is capable of demuxing the container specified by mimeType, and the codec(s) specified by mimeType are supported, all as specified by capability "foo".
    MediaKeys.isTypeSupported("com.example.somesystem", initDataType, mimeType, "foo")
    Returns whether the user agent probably supports Clear Key using the Initialization Data format initDataType and the container and codec(s) specified by mimeType.
    "probably" == MediaKeys.isTypeSupported("org.w3.clearkey", initDataType, mimeType)
ParameterTypeNullableOptionalDescription
keySystemDOMstring
initDataTypeDOMString
contentTypeDOMString
capabilityDOMString
Return type: IsTypeSupportedResult

When this method is invoked, the user agent must run the following steps:

  1. If keySystem is an empty string or contains an unrecognized or unsupported Key System, return "" and abort these steps. String comparison is case-sensitive.

  2. If the keySystem implementation is not available and usable, return "" and abort these steps.

  3. Follow the steps for the first matching condition from the following list:

    If keySystem is a value that may be successfully passed to create()
    Let probably result be "probably".
    Otherwise
    Let probably result be "maybe".
    Note

    This may, for example, be the case for strings that are only used for discovery.

  4. If initDataType was not provided, follow the steps for the first matching condition from the following list and abort these steps:

    If the user agent is not confident that the keySystem implementation is available and usable
    Return "maybe".
    Otherwise
    Return probably result.
  5. If initDataType is an empty string or contains an unrecognized or unsupported Initialization Data, return "" and abort these steps. String comparison is case-sensitive.

  6. If initDataType is not an Initialization Data supported by the keySystem implementation, return "" and abort these steps.

  7. If the keySystem implementation supporting initDataType is not available and usable, return "" and abort these steps.

  8. If contentType was not provided, follow the steps for the first matching condition from the following list and abort these steps:

    If the user agent is not confident that the keySystem implementation supporting initDataType is available and usable
    Return "maybe".
    Otherwise
    Return probably result.
  9. If contentType is an empty string or contains an invalid or unrecognized MIME type, return "" and abort these steps.

  10. Let container be the container type specified by contentType.

  11. Let parameters be the RFC 6381 [RFC6381] parameters, if any, specified by contentType.

  12. Let media types be the set of media types specified by parameters. It MAY be empty. The case-sensitivity of string comparisons is determined by the appropriate RFC or other specification.

    Note

    For example, all of the codecs. Case-sensitive string comparison is RECOMMENDED because RFC 6381 [RFC6381] says, "Values are case sensitive" for some formats.

  13. If the user agent does not support container, return "" and abort these steps. The case-sensitivity of string comparisons is determined by the appropriate RFC.

    Note

    Per RFC 6838 [RFC6838], "Both top-level type and subtype names are case-insensitive."

  14. If the user agent and keySystem implementation do not support playback of encrypted media data for all media types, return "" and abort these steps.

  15. If capability was not provided, follow the steps for the first matching condition from the following list and abort these steps:

    If the user agent is not confident that the keySystem implementation supporting initDataType is available and usable to decrypt and enable playback of support playback of encrypted media data for all media types
    Return "maybe".
    If the user agent did not recognize one or more parameters
    Return "maybe".
    Otherwise
    Return probably result.
  16. If capability is an empty string, return "" and abort these steps.

  17. If the keySystem implementation does not support capability, return "" and abort these steps. String comparison is case-sensitive.

  18. Follow the steps for the first matching condition from the following list:

    If the user agent is not confident that the keySystem implementation supporting initDataType is available and usable to decrypt and enable playback of support playback of encrypted media data for all media types as specified by capability
    Return "maybe".
    If the user agent did not recognize one or more parameters
    Return "maybe".
    Otherwise
    Return probably result.
setServerCertificate

Provides a server certificate to be used to encrypt messages to the license server.

Note

The contents of serverCertificate are Key System-specific.

Note

Key systems that use such certificates should also support requesting the certificate from the server via an additional round trip. This simplifies interoperability for applications that are not designed to provide certificates to specific key systems.

ParameterTypeNullableOptionalDescription
serverCertificate(ArrayBuffer or ArrayBufferView)
Return type: Promise<void>

When this method is invoked, the user agent must run the following steps:

  1. If serverCertificate is an empty array, return a promise rejected with a new a new DOMException whose name is InvalidAccessError.

  2. If the CDM corresponding to the keySystem attribute does not support server certificates, return a promise rejected with a new DOMException whose name is NotSupportedError.

  3. Let certificate be a copy of the contents of the serverCertificate parameter.

  4. Let promise be a new promise.

  5. Run the following steps asynchronously:

    1. Let cdm be the cdm during the initialization of this object.

    2. Use the cdm to process certificate.

    3. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name.

    4. Resolve promise.

  6. Return promise.

3. MediaKeySession Object

The MediaKeySession object represents a key session.

For methods that return a promise, all errors are reported asynchronously by rejecting the returned Promise. This includes WebIDL type mapping errors.

The steps of an algorithm are always aborted when resolving or rejecting a promise.

interface MediaKeySession : EventTarget {
    readonly    attribute DOMString           sessionId;
    readonly    attribute unrestricted double expiration;
    readonly    attribute Promise<void>       closed;
    Promise<void>                  generateRequest (DOMString initDataType, (ArrayBuffer or ArrayBufferView) initData);
    Promise<boolean>               load (DOMString sessionId);
    Promise<void>                  update ((ArrayBuffer or ArrayBufferView) response);
    Promise<void>                  close ();
    Promise<void>                  remove ();
    Promise<sequence<ArrayBuffer>> getUsableKeyIds ();
};

3.1 Attributes

closed of type Promise<void>, readonly

Signals when object becomes closed as a result of the session close algorithm being run. This promise can only be fulfilled and is never rejected.

expiration of type unrestricted double, readonly

The time, in milliseconds since 01 January, 1970 UTC, after which the key(s) in the session will no longer be usable to decrypt media data, or NaN if no such time exists, as determined by the CDM.

sessionId of type DOMString, readonly

The Session Id for this object and the associated key(s) or license(s).

3.2 Methods

close

Indicates that the application no longer needs the session and the CDM should release any resources associated with this object and close it.

Note

The returned promise is resolved when the request has been processed, and the closed attribute promise is resolved when the session is closed.

No parameters.
Return type: Promise<void>

When this method is invoked, the user agent must run the following steps:

  1. If this object's callable value is false, return a promise rejected with a new DOMException whose name is InvalidStateError.

  2. If the session close algorithm has been run on this object, return a resolved promise.

  3. Let promise be a new promise.

  4. Run the following steps asynchronously:

    1. Let cdm be the CDM loaded during the initialization of the MediaKeys object that created this object.

    2. Use the cdm to execute the following steps:

      1. Process the close request.

        Do not remove stored session data.

      2. If the previous step caused the session to be closed, run the session close algorithm on this object.

    3. Resolve promise.

  5. Return promise.

generateRequest

Generates a request based on the initData.

initData is Initialization Data, and initDataType is an Initialization Data that indicates how to interpret initData.

ParameterTypeNullableOptionalDescription
initDataTypeDOMString
initData(ArrayBuffer or ArrayBufferView)
Return type: Promise<void>

When this method is invoked, the user agent must run the following steps:

  1. If this object's uninitialized value is false, return a promise rejected with a new DOMException whose name is InvalidStateError.

  2. Let this object's uninitialized be false.

  3. If initDataType is an empty string, return a promise rejected with a new DOMException whose name is InvalidAccessError.

  4. If initData is an empty array, return a promise rejected with a new DOMException whose name is InvalidAccessError.

  5. Let media keys be the MediaKeys object that created this object.

  6. If the CDM corresponding to media keys's keySystem attribute does not support initDataType as an Initialization Data, return a promise rejected with a new DOMException whose name is NotSupportedError. String comparison is case-sensitive.

  7. Let init data be a copy of the contents of the initData parameter.

  8. Let session type be this object's session type.

  9. Let promise be a new promise.

  10. Run the following steps asynchronously:

    1. If the init data is not valid for initDataType, reject promise with a new DOMException whose name is InvalidAccessError.

    2. Let sanitized init data be a validated and/or sanitized version of init data.

      Note

      The user agent should thoroughly validate the Initialization Data before passing it to the CDM. This may include verifying values are within reasonable limits, stripping irrelevant data or fields, pre-parsing it, sanitizing it, and/or generating a fully sanitized version. The user agent should check that the length and values of fields are reasonable. Unknown fields should be rejected or removed. For Initialization Data formats that support multiple entries, the user agent should remove entries that are not needed by the CDM.

    3. If the previous step failed, reject promise with a new DOMException whose name is InvalidAccessError.

    4. Let session id be the empty string.

    5. Let message be null.

    6. Let cdm be the CDM loaded during the initialization of media keys.

    7. Use the cdm to execute the following steps:

      1. If the init data is not supported by the cdm, reject promise with a new DOMException whose name is NotSupportedError.

      2. Follow the steps for the first matching condition from the following list:

        If session type is "temporary"
        Let requested session type be a temporary non-persisted session.

        Note

        The returned license must not be persistable.

        If session type is "persistent"
        Let requested session type be a persistable session.

        Note

        The returned license may be persistable.)

        Note

        The license server determines the type of license that is returned, either persistent or non-persistent. A persistent license cannot be added to a non-persistable session.

      3. Let session id be a unique Session Id string.

        If session type is "persistent", the ID MUST be unique within the the origin of this object's Document over time, including across Documents and browsing sessions.

      4. Let message be a request for the requested session type generated based on the init data, which is interpreted per initDataType.

        Note

        For example, a license request.

        The cdm MUST NOT use any stream-specific data, including media data, not provided via the init data.

        The cdm SHOULD NOT store session data, including the session ID, at this point. See Session Storage and Persistence.

    8. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name.

    9. Set the sessionId attribute to session id.

    10. Let this object's callable be true.

    11. Run the queue a "message" event algorithm on the session, providing "licenserequest" and message.

    12. Resolve promise.

  11. Return promise.

getUsableKeyIds
Issue 2

Bug 26372 - getUsableKeyIds() may be replaced.

Gets the key IDs for keys in the session that the CDM knows are currently usable to decrypt media data.

No parameters.
Return type: Promise<sequence<ArrayBuffer>>

When this method is invoked, the user agent must run the following steps:

  1. If this object's callable value is false, return a promise rejected with a new DOMException whose name is InvalidStateError.

  2. Let promise be a new promise.

  3. Run the following steps asynchronously:

    1. Let cdm be the CDM loaded during the initialization of the MediaKeys object that created this object.

    2. Use the cdm to execute the following steps:

      1. Let usable key ids be a list of the key IDs for keys in the session that the CDM knows are currently usable to decrypt media data. usable key ids MUST NOT contain IDs for keys that may not currently be usable. Each element MUST be unique.

    3. If the preceding step failed, reject promise with a new DOMException whose name is the appropriate error name.

    4. Resolve promise with a new Sequence object, each element of which contains one element of usable key ids.

load

Loads the data stored for the sessionId into the session represented by the object.

ParameterTypeNullableOptionalDescription
sessionIdDOMString
Return type: Promise<boolean>

When this method is invoked, the user agent must run the following steps:

  1. If this object's uninitialized value is false, return a promise rejected with a new DOMException whose name is InvalidStateError.

  2. Let this object's uninitialized be false.

  3. If sessionId is an empty string, return a promise rejected with a new DOMException whose name is InvalidAccessError.

  4. If this object's session type is not "persistent", return a promise rejected with a new DOMException whose name is InvalidAccessError.

  5. Let media keys be the MediaKeys object that created this object.

  6. If the CDM corresponding to media keys's keySystem attribute does not support loading previous sessions, return a promise rejected with a new DOMException whose name is NotSupportedError.

  7. Let promise be a new promise.

  8. Run the following steps asynchronously:

    1. Let sanitized session ID be a validated and/or sanitized version of sessionId.

      Note

      The user agent should thoroughly validate the sessionId value before passing it to the CDM. At a minimum, this should include checking that the length and value (e.g. alphanumeric) are reasonable.

    2. If the previous step failed, reject promise with a new DOMException whose name is InvalidAccessError.

    3. Let expiration time be NaN.

    4. Let message be null.

    5. Let message type be null.

    6. Let origin be the origin of this object's Document.

    7. Let cdm be the CDM loaded during the initialization of media keys.

    8. Use the cdm to execute the following steps:

      1. If there is no data stored for the sanitized session ID in the origin, resolve promise with false.

      2. Let session data be the data stored for the sanitized session ID in the origin. This MUST NOT include data from other origin(s) or that is not associated with an origin.

      3. If there is an unclosed "persistent" session in any Document representing the session data, reject promise with a new DOMException whose name is QuotaExceededError.

        Note

        In other words, do not create a session if a non-closed persistent session already exists for this sanitized session ID in any browsing context.

      4. Load the session data.

      5. If the session data indicates an expiration time for the session, let expiration time be the expiration time in milliseconds since 01 January 1970 UTC.

      6. If the CDM needs to send a message:

        1. Let message be a message generated by the CDM based on the session data.

        2. Let message type be the appropriate MediaKeyMessageType for the message.

    9. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name.

    10. Set the sessionId attribute to sanitized session ID.

    11. Let this object's callable be true.

    12. If the loaded session contains usable keys, run the usable keys changed algorithm on the session.

      The algorithm MAY also be run later should additional processing be necessary to determine with certainty whether one or more keys is usable.

    13. Run the update expiration algorithm on the session, providing expiration time.

    14. If message is not null, run the queue a "message" event algorithm on the session, providing message type and message.

    15. Resolve promise with true.

  9. Return promise.

remove

Removes stored session data associated with this object.

No parameters.
Return type: Promise<void>

When this method is invoked, the user agent must run the following steps:

  1. If this object's callable value is false, return a promise rejected with a new DOMException whose name is InvalidStateError.

  2. If this object's session type is not "persistent", return a promise rejected with a new DOMException whose name is InvalidAccessError.

  3. If the session close algorithm has been run on this object, return a promise rejected with a new DOMException whose name is InvalidStateError.

  4. Let promise be a new promise.

  5. Run the following steps asynchronously:

    1. Let cdm be the CDM loaded during the initialization of the MediaKeys object that created this object.

    2. Use the cdm to execute the following steps:

      1. Process the remove request.

        This MAY involve exchanging message(s) with the application.

        Unless this step fails, the CDM MUST have cleared all stored session data associated with this object, including the sessionId, before proceeding to the next step.

        Note

        A subsequent call to load() with the value sessionId would fail because there is no data stored for that session ID.)

    3. Run the following steps asynchronously once the above step has completed:

      1. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name.

      2. Run the session close algorithm on this object.

      3. Resolve promise.

  6. Return promise.

update

Provides messages, including licenses, to the CDM.

Note

The contents of response are Key System-specific.

ParameterTypeNullableOptionalDescription
response(ArrayBuffer or ArrayBufferView)
Return type: Promise<void>

When this method is invoked, the user agent must run the following steps:

  1. If this object's callable value is false, return a promise rejected with a new DOMException whose name is InvalidStateError.

  2. If response is an empty array, return a promise rejected with a new DOMException whose name is InvalidAccessError.

  3. Let response copy be a copy of the contents of the response parameter.

  4. Let promise be a new promise.

  5. Run the following steps asynchronously:

    1. Let sanitized response be a validated and/or sanitized version of response copy.

      Note

      The user agent should thoroughly validate the response before passing it to the CDM. This may include verifying values are within reasonable limits, stripping irrelevant data or fields, pre-parsing it, sanitizing it, and/or generating a fully sanitized version. The user agent should check that the length and values of fields are reasonable. Unknown fields should be rejected or removed.

    2. If the previous step failed, reject promise with a new DOMException whose name is InvalidAccessError.

    3. Let cdm be the CDM loaded during the initialization of the MediaKeys object that created this object.

    4. Let message be null.

    5. Let message type be null.

    6. Use the cdm to execute the following steps:

      1. If the format of sanitized response is invalid in any way, reject promise with a new DOMException whose name is InvalidAccessError.

      2. Process sanitized response, following the stipulation for the first matching condition from the following list:

        If sessionType is "temporary" and sanitized response does not specify that session data, including any license, key(s), or similar session data it contains, should be stored
        Continue processing sanitized response, not storing any session data.
        If sessionType is "persistent"
        Continue processing sanitized response, storing the license, key(s), or similar session data contained in sanitized response as permitted or instructed by the license. Such data MUST be stored such that only the origin of this object's Document can access it.
        Otherwise
        Reject promise with a new DOMException whose name is InvalidAccessError.

        See also Session Storage and Persistence.

        Note

        When sanitized response contains key(s) and/or related data, cdm will likely cache the key and related data indexed by key ID.

        Note

        The replacement algorithm within a session is Key System-dependent.

        Keys from different sessions SHOULD be cached independently such that closing one session does not affect keys in other sessions, even if they have overlapping key IDs.

        Note

        It is RECOMMENDED that CDMs support a standard and reasonably high minimum number of keys per MediaKeySession object, including a standard replacement algorithm, and a standard and reasonably high minimum number of MediaKeySession objects. This enables a reasonable number of key rotation algorithms to be implemented across user agents and may reduce the likelihood of playback interruptions in use cases that involve various streams in the same element (i.e. adaptive streams, various audio and video tracks) using different keys.

      3. If the set of usable keys changed, run the usable keys changed algorithm on the session.

        The algorithm MAY also be run later should additional processing be necessary to determine with certainty whether one or more keys is usable.

      4. If the expiration time for the session changed, run the update expiration algorithm on the session, providing the new expiration time.

      5. If a message needs to be sent to the server, execute the following steps:

        1. Let message be that message.

        2. Let message type be the appropriate MediaKeyMessageType for the message.

    7. If any of the preceding steps failed, reject promise with a new DOMException whose name is the appropriate error name.

    8. If message is not null, run the queue a "message" event algorithm on the session, providing message type and message.

    9. Resolve promise.

  6. Return promise.

3.3 MediaKeyMessageEvent

The MediaKeyMessageEvent object is used for the message event.

Events are constructed as defined in Constructing events [DOM].

enum MediaKeyMessageType {
    "licenserequest",
    "licenserenewal",
    "licenserelease"
};
Enumeration description
licenserequestThe message contains a request for a new license.
licenserenewalThe message contains a request to renew an existing license.
licensereleaseThe message contains a proof of key release.
[ Constructor (DOMString type, optional MediaKeyMessageEventInit eventInitDict)]
interface MediaKeyMessageEvent : Event {
    readonly    attribute MediaKeyMessageType type;
    readonly    attribute ArrayBuffer         message;
};

3.3.1 Constructors

MediaKeyMessageEvent
ParameterTypeNullableOptionalDescription
typeDOMString
eventInitDictMediaKeyMessageEventInit

3.3.2 Attributes

message of type ArrayBuffer, readonly
The message from the CDM. Messages are Key System-specific.
type of type MediaKeyMessageType, readonly
The type of the message.

3.3.3 MediaKeyMessageEventInit

dictionary MediaKeyMessageEventInit : EventInit {
    MediaKeyMessageType type = "licenserequest";
    ArrayBuffer         message = ArrayBuffer();
};
3.3.3.1 Dictionary MediaKeyMessageEventInit Members
message of type ArrayBuffer, defaulting to ArrayBuffer()
The message.
type of type MediaKeyMessageType, defaulting to "licenserequest"
The type of the message.

3.4 Event Summary

This section is non-normative.

Note

In some implementations, MediaKeySession objects may not fire any events until the MediaKeys object is associated with a media element using setMediaKeys().

Event name Interface Dispatched when...
keyschange Event There has been a change in usable keys in the session.
message MediaKeyMessageEvent The CDM has generated a message for the session.

3.5 Algorithms

3.5.1 Queue a "message" Event

The Queue a "message" Event algorithm is run when the CDM needs to queue a message event to a MediaKeySession object. Requests to run this algorithm include a target MediaKeySession object, a message type, and a message.

The following steps are run:

  1. Let the session be the specified MediaKeySession object.

  2. Queue a task to fire a simple event named message at the session.

    The event is of type MediaKeyMessageEvent and has:

    • type = the specified message type

      message = the specified message

3.5.2 Usable Keys Changed

The Usable Keys Changed algorithm is run when the CDM changes the set of keys in the session that may be used for decryption. This can happen as the result of an update() call or some other event. Requests to run this algorithm include a target MediaKeySession object.

The following steps are run:

  1. Let the session be the associated MediaKeySession object.

  2. Queue a task to fire a simple event named keyschange at the session.

  3. Queue a task to run the attempt to resume playback if necessary algorithm on each of the media element(s) whose mediaKeys attribute is the MediaKeys object that created the session.

    The user agent MAY choose to skip this step if it knows resuming will fail.

    Note

    For example, the user agent may skip this step if no additional keys became available.

3.5.3 Update Expiration

The Update Expiration algorithm is run when the CDM changes the expiration time of a session. This can happen as the result of an update() call or some other event. Requests to run this algorithm include a target MediaKeySession object and the new expiration time, which may be NaN.

The following steps are run:

  1. Let the session be the associated MediaKeySession object.

  2. Let expiration time be NaN.

  3. If the new expiration time is not NaN, let expiration time be the new expiration time in milliseconds since 01 January 1970 UTC.

  4. Set the session's expiration attribute to expiration time.

3.5.4 Session Close

The Session Close algorithm is run when the CDM closes the session associated with a MediaKeySession object.

Note

The CDM may close a session at any point, such as in response to a close() call, when the session is no longer needed, or when system resources are lost. Keys in other sessions SHOULD be unaffected, even if they have overlapping key IDs.

The following steps are run:

  1. Let the session be the associated MediaKeySession object.

  2. Let media keys be the MediaKeys object that created this object.

  3. Let promise be the closed attribute of the session.

  4. Resolve promise.

3.6 Exceptions

The methods report errors by rejecting the returned promise with a DOMException. The following DOMException names from WebIDL are used in the algorithms. Causes specified specified in the algorithms are listed alongside each name, though these names MAY be used for other reasons as well.

Name Possible Causes (non-exhaustive)
NotSupportedError The existing MediaKeys object cannot be removed.
The key system is not supported.
The key system is not supported on insecure origins.
The initialization data type is not supported by the key system.
The session type is not supported by the key system.
The initialization data is not supported by the key system.
The operation is not supported by the key system.
InvalidStateError The existing MediaKeys object cannot be removed at this time.
The session has already been used.
The session is not yet initialized.
The session is closed.
InvalidAccessError The parameter is empty.
Invalid initialization data.
The operation is not supported on sessions of this type.
Invalid response format.
A persistent license was provided for a "temporary" session.
QuotaExceededError The MediaKeys object cannot be used with additional HTMLMediaElements.
A non-closed session already exists for this sessionId.

3.7 Session Storage and Persistence

This section provides an overview of session stroage and persistence that complements the algorithms.

If this object's session type is not "persistent", the user agent and CDM MUST NOT persist a record of or data related to the session at any point. This includes license(s), key(s), and the Session Id.

The remainder of this section applies to "persistent" sessions, which are OPTIONAL for implementatations to support.

Persisted data MUST always be stored such that only the origin of this object's Document can access it. In addition, the data MUST only be accessible by the current profile of this user agent - other user agent profiles, user agents, and applications MUST NOT be able to access the stored data.

The CDM SHOULD NOT store session data, including the Session ID, until update() is called the first time. Specifically, the CDM SHOULD NOT store session data during the generateRequest() algorithm. This ensures that the application is aware of the session and knows it needs to eventually remove it.

The CDM MUST ensure that data for a given session is only present in one active unclosed session in any Document. In other words, load() MUST fail when there is already a MediaKeySession representing the session specified by the sessionId parameter, either because the object that created it via generateRequest() is still active or it has been loaded into another object via load(). A session MAY only be loaded again after the session close algorithm has not been run on the object representing it.

An application that creates a "persistent" session SHOULD later remove the stored data using remove(). The CDM MAY also remove sessions as appropriate, but applications SHOULD NOT rely on this.

Note

See the Security Considerations and Privacy Considerations sections for additional considerations when supporting persistent storage.

4. HTMLMediaElement Extensions

This section specifies additions to and modifications of the HTMLMediaElement [HTML5] when the Encrypted Media Extensions are supported.

For methods that return a promise, all errors are reported asynchronously by rejecting the returned Promise. This includes WebIDL type mapping errors.

The steps of an algorithm are always aborted when resolving or rejecting a promise.

enum MediaWaitingFor {
    "none",
    "data",
    "key"
};
Enumeration description
none The media element is not waiting for anything.
data The media element is not waiting for data.
key The media element is not waiting for a key.
partial interface HTMLMediaElement : EventTarget {
    readonly    attribute MediaKeys?      mediaKeys;
    readonly    attribute MediaWaitingFor waitingFor;
                attribute EventHandler    onencrypted;
    Promise<void> setMediaKeys (MediaKeys? mediaKeys);
};

4.1 Attributes

mediaKeys of type MediaKeys, readonly , nullable

The MediaKeys being used when decrypting encrypted media data for this media element.

onencrypted of type EventHandler,

Event handler for the encrypted event MUST be supported by all HTMLMediaElements as both a content attribute and an IDL attribute.

waitingFor of type MediaWaitingFor, readonly

Indicates what the media element is waiting for, if anything (indicated by the waiting and canplay events). This is described in the encrypted block encountered algorithm.

4.2 Methods

setMediaKeys

Provides the MediaKeys to use when decrypting media data during playback.

Note

Support for clearing or replacing the associated MediaKeys object during playback is a quality of implementation issue. In many cases it will result in a bad user experience or rejected promise.

Note

As a best practice, applications should create a MediaKeys object and call setMediaKeys() before providing media data (for example, setting the src attribute). This avoids potential delays in some implementations.

ParameterTypeNullableOptionalDescription
mediaKeysMediaKeys
Return type: Promise<void>

When this method is invoked, the user agent must run the following steps:

  1. If mediaKeys and the mediaKeys attribute are the same object, return a resolved promise.

  2. If this object's attaching media keys value is true, return a promise rejected with a new DOMException whose name is InvalidStateError.

  3. Let this object's attaching media keys value be true.

  4. Let promise be a new promise.

  5. Run the following steps asynchronously:

    1. If mediaKeys is not null, it is already in use by another media element, and the user agent is unable to use it with this element, let this object's attaching media keys value be false and reject promise with a new DOMException whose name is QuotaExceededError.

    2. If the mediaKeys attribute is not null, run the following steps:

      1. If the user agent or CDM do not support removing the association, let this object's attaching media keys value be false and reject promise with a new DOMException whose name is NotSupportedError.

      2. If the association cannot currently be removed, let this object's attaching media keys value be false and reject promise with a new DOMException whose name is InvalidStateError.

        Note

        For example, some implementations may not allow removal during playback.

      3. Stop using the CDM instance represented by the mediaKeys attribute to decrypt media data and remove the association with the media element.

      4. If the preceding step failed, let this object's attaching media keys value be false and reject promise with a new DOMException whose name is the appropriate error name.

    3. If mediaKeys is not null, run the following steps:

      1. Associate the CDM instance represented by mediaKeys with the media element for decrypting media data.

      2. If the preceding step failed, run the following steps:

        1. Set the mediaKeys attribute to null.

        2. Let this object's attaching media keys value be false.

        3. Reject promise with a new DOMException whose name is the appropriate error name.

      3. Queue a task to run the attempt to resume playback if necessary algorithm on the media element.

        The user agent MAY choose to skip this step if it knows resuming will fail.

        Note

        For example, the user agent may skip this step if mediaKeys has no sessions.

    4. Set the mediaKeys attribute to mediaKeys.

    5. Let this object's attaching media keys value be false.

    6. Resolve promise.

  6. Return promise.

4.3 MediaEncryptedEvent

The MediaEncryptedEvent object is used for the encrypted event.

Events are constructed as defined in Constructing events [DOM].

[ Constructor (DOMString type, optional MediaEncryptedEventInit eventInitDict)]
interface MediaEncryptedEvent : Event {
    readonly    attribute DOMString    initDataType;
    readonly    attribute ArrayBuffer? initData;
};

4.3.1 Constructors

MediaEncryptedEvent
ParameterTypeNullableOptionalDescription
typeDOMString
eventInitDictMediaEncryptedEventInit

4.3.2 Attributes

initData of type ArrayBuffer, readonly , nullable
The Initialization Data for the event.
initDataType of type DOMString, readonly
Indicates the Initialization Data of the Initialization Data contained in the initData attribute.

4.3.3 MediaEncryptedEventInit

dictionary MediaEncryptedEventInit : EventInit {
    DOMString    initDataType = "";
    ArrayBuffer? initData = null;
};
4.3.3.1 Dictionary MediaEncryptedEventInit Members
initData of type ArrayBuffer, nullable, defaulting to null
The Initialization Data.
initDataType of type DOMString, defaulting to ""
The Initialization Data.

4.4 Event Summary

This section is non-normative.

Event name Interface Dispatched when... Preconditions
encrypted MediaEncryptedEvent The user agent encounters Initialization Data in the media data. readyState is equal to HAVE_METADATA or greater.
Note

It is possible that the element is playing or has played.

4.5 Algorithms

4.5.1 Initialization Data Encountered

The following steps are run when the media element encounters Initialization Data in the media data during the resource fetch algorithm:

  1. Let initDataType be the empty string.

  2. Let initData be null.

  3. If the media data is CORS-same-origin and not mixed content, run the following steps:

    1. Let initDataType be the string representing the Initialization Data of the Initialization Data.

    2. Let initData be the Initialization Data.

    Note

    While the media element may allow loading of "Optionally-blockable Content" [MIXED-CONTENT], the user agent MUST NOT expose Initialization Data from such media data to the application.

  4. Queue a task to fire a simple event named encrypted at the media element.

    The event is of type MediaEncryptedEvent and has:

    Note

    readyState is not changed and no algorithms are aborted. This event merely provides information.

    Note

    The initData attribute will be null if the media data is not CORS-same-origin or is mixed content. Applications may retrieve the Initialization Data from an alternate source.

  5. Continue Normal Flow: Continue with the existing media element's resource fetch algorithm.

4.5.2 Encrypted Block Encountered

The following steps are run when the media element encounters a block of encrypted media data during the resource fetch algorithm:

  1. If the media element's mediaKeys attribute is not null, run the following steps:

    1. Let media keys be the MediaKeys object referenced by that atribute.

    2. Let cdm be the CDM loaded during the initialization of the media keys.

    3. If there is at least one MediaKeySession created by the media keys on which the session close algorithm has not been run, run the following steps:

      Note

      This check ensures the cdm has finished loading and is a prequisite for a matching key being available.

      1. Let the block key ID be the key ID of the current block.

        Note

        The key ID is generally specified by the container.

      2. Use the cdm to execute the following steps:

        1. Let available keys be the union of keys in sessions that were created by the media keys.

        2. Follow the steps for the first matching condition from the following list:

          If any of the available keys corresponds to the block key ID and is usable
          Run the following steps:
          1. Let block key be the matching key.

            Note

            If multiple sessions contain a usable key for the block key ID, which key to use is Key System-dependent.

          2. Use the cdm to decrypt the block using block key.

          3. Follow the steps for the first matching condition from the following list:

            If decryption fails
            Abort the media element's resource fetch algorithm, run the steps to report a MEDIA_ERR_DECODE error, and abort these steps.
            Otherwise
            Run the following steps:
            1. If the waitingFor attribute on the media element is "key", set the waitingFor attribute on the media element to "none".

            2. Abort these steps and process the decrypted block as normal.

              Note

              In other words, decode the block.

            Note

            Not all decryption problems (i.e. using the wrong key) will result in a decryption failure. In such cases, no error is fired here but one may be fired during decode.

          If any of the available keys corresponds to the block key ID and is unusable
          Issue 3

          Bug 26372 - It is TBD whether anything should happen in this case.

          Note

          Otherwise, there is no key for the block key ID in any session so continue.

  2. Run the following steps:

    Note

    These steps are reached when there is no usable key for the block.

    1. Run the queue a "waiting" event algorithm on the media element.

    2. Wait for a signal to resume playback.

Note

For frame-based encryption, this may be implemented as follows when the media element attempts to decode a frame as part of the resource fetch algorithm:

  1. Let encrypted be false.

  2. Detect whether the frame is encrypted.

    If the frame is encrypted
    Run the steps above.
    Otherwise
    Continue.
  3. Decode the frame.

  4. Provide the frame for rendering.

4.5.3 Queue a "waiting" Event

The Queue a "waiting" Event algorithm is run when the CDM needs to queue a waiting event to a HTMLMediaElement object. Requests to run this algorithm include a target HTMLMediaElement object.

The following steps are run:

  1. Let the media element be the specified HTMLMediaElement object.

  2. If the waitingFor attribute on the media element is not "key", run the following steps:

    1. Set the waitingFor attribute on the media element to "key".

    2. Queue a task to fire a simple event named waiting at the media element.

  3. Suspend playback.

4.5.4 Attempt to Resume Playback If Necessary

The Attempt to Resume Playback If Necessary algorithm is run when one or more keys becomes available. If playback is blocked waiting for a key, it resumes playback if a necessary key has been provided. Requests to run this algorithm include a target HTMLMediaElement object.

The following steps are run:

  1. Let the media element be the specified HTMLMediaElement object.

  2. If the waitingFor attribute on the media element is not "key", abort these steps.

  3. Attempt to resume the resource fetch algorithm by running the encrypted block encountered algorithm.

  4. If the user agent can advance the current playback position in the direction of playback, run the following steps:

    1. Queue a task to fire a simple event named canplay at the media element.

    2. If the paused attribute on the media element is false, queue a task to fire a simple event named playing at the media element.

    Otherwise, the waitingFor attribute on the media element MUST NOT be "none".

4.5.5 Playing the Media Resource Algorithm Modifications

The following steps are added to readyState change algorithms in Playing the media resource:

4.6 Media Element Restrictions

This section is non-normative.

Media data processed by a CDM MAY be unavailable through Javascript APIs in the usual way (for example using the CanvasRenderingContext2D drawImage() method and the AudioContext MediaElementAudioSourceNode). This specification does not define conditions for such non-availability of media data, however, if media data is not available to Javascript APIs then these APIs MAY behave as if no media data was present at all.

Where media rendering is not performed by the UA, for example in the case of a hardware protected media pipeline, then the full set of HTML rendering capabilities, for example CSS Transforms, MAY be unavailable. One likely restriction is that video media MAY be constrained to appear only in rectangular regions with sides parallel to the edges of the window and with normal orientation.

5. Common Key Systems

All user agents MUST support the common key systems described in this section.

Note

This ensures that there is a common baseline level of protection that is guaranteed to be supported in all user agents, including those that are entirely open source. Thus, content providers that need only basic protection can build simple applications that will work on all platforms without needing to work with any content protection providers.

5.1 Clear Key

The "org.w3.clearkey" Key System uses plain-text clear (unencrypted) key(s) to decrypt the source. No additional client-side content protection is required. This Key System is described below.

5.1.1 Capabilities

The following describe how Clear Key supports key system-specific capabilities:

5.1.2 Behavior

The following describe how Clear Key implements key system-specific behaviors:

  • In the generateRequest() algorithm:

    • The generated message is a JSON object encoded in UTF-8 as described in License Request Format.

    • The request is generated by extracting the key IDs from the init data.

    • The "type" member value is the value of the sessionType parameter.

  • The sessionId attribute is a numerical value representable by a 32-bit integer.

  • The expiration attribute is always NaN.

  • In the update() algorithm:

    • The response parameter is a JWK Set as described in License Format.

    • sanitized response is considered invalid if it is not a valid JWK Set with at least one valid JWK key of a valid length for the media type.

  • The getUsableKeyIds() method always returns all key IDs that have been provided via update().

  • Initialization Data: Implementations MAY support any combination of registered Initialization Data types [EME-REGISTRY]. Implementations SHOULD support the "keyids" type and other types appropriate for content types supported by the user agent.

5.1.3 License Request Format

This section describes the format of the license request provided to the application via the message attribute of the message event.

The format is a JSON object containing the following members:

"kids"
An array of key IDs. Each element of the array is the base64url encoding of the octet sequence containing the key ID value.
"type"
The requested SessionType

When contained in the ArrayBuffer message attribute of a MediaKeyMessageEvent object, the JSON string is encoded in UTF-8 as specified in the Encoding specification [ENCODING]. Applications MAY decode the contents of the ArrayBuffer to a JSON string using the TextDecoder interface [ENCODING].

5.1.3.1 Example

This section is non-normative.

The following example is a license request for a temporary license for two key IDs. (Line breaks are for readability only.)

Example 1
{
  "kids": 
    [
     "67ef0gd8pvfd0",
     "77ef0gd8pvfd0"
    ],
  "type":"temporary"
}

5.1.4 License Format

This section describes the format of the license to be provided via the response parameter of the update() method.

The format is a JSON Web Key (JWK) Set containing representation of the symmetric key to be used for decryption, as defined in the JSON Web Key (JWK) specification [JWK].

For each JWK in the set, the parameter values are as follows:

"kty" (key type)
"oct" (octet sequence)
"alg" (algorithm)
"A128KW" (AES key wrap using a 128-bit key)
"k" (key value)
The base64url encoding of the octet sequence containing the symmetric key value
"kid" (key ID)
The base64url encoding of the octet sequence containing the key ID value

The JSON object MAY have an optional "type" member value, which MUST be one of the SessionType values. If not specified, the default value of "temporary" is used. The update() algorithm compares this value to the sessionType.

When passed to the update() method as the ArrayBuffer response parameter, the JSON string MUST be encoded in UTF-8 as specified in the Encoding specification [ENCODING]. Applications MAY encode the JSON string using the TextEncoder interface [ENCODING].

5.1.4.1 Example

This section is non-normative.

The following example is a JWK Set containing a single symmetric key. (Line breaks are for readability only.)

Example 2
{
  "keys": 
    [{
      "kty":"oct",
      "alg":"A128KW",
      "k":"GawgguFyGrWKav7AX4VKUg"
      "kid":"67ef0gd8pvfd0",
    }],
  "type":"temporary"
}

5.1.5 Using base64url

This section is non-normative.

For more information on base64url and working with it, see the "Base64url Encoding" terminology definition and "Notes on implementing base64url encoding without padding" in [JWS]. Specifically, there is no '=' padding, and the characters '-' and '_' MUST be used instead of '+' and '/', respectively.

6. Security Considerations

This section is non-normative.

User Agent and Key System implementations must consider media data, Initialization Data, responses (i.e. data passed to update()), licenses, key data, and all other data provided by the application as untrusted content and potential attack vectors. They must use appropriate safeguards to mitigate any associated threats and take care to safely parse, decrypt, etc. such data. User Agents may want to validate data before passing it to the CDM, especially if the CDM does not run in the same (sandboxed) context as the DOM (i.e. rendering).

Implementations should not return active content or passive content that affects program control flow to the application. For example, it is not safe to expose URLs or other information that may have come from media data, such as is the case for the Initialization Data passed to generateRequest(). Applications should determine the URLs to use. The type attribute of the message event can be used by the application to select among a set of URLs if applicable.

User Agents are responsible for providing users with a secure way to browse the web. Since User Agents may integrate with third party CDM implementations, CDM implementers must provide sufficient information and controls to user agent implementers to enable them to properly asses the security implications of integrating with the Key System.

Unsandboxed CDMs (or CDMs that use platform features) and UAs that use them must be especially careful in all areas of security, including parsing of key and media data, etc. due to the potential for compromises to provide access to OS/platform features, interact with or run as root, access drivers, kernel, firmware, hardware, etc., all of which may not be written to be robust against hostile software or web-based attacks. Additionally, CDMs may not be updated with security fixes as frequently, especially when part of the OS, platform or hardware.

User agents may choose to only support the EME APIs and/or specific Key Systems (i.e. based on privacy and security risks) on secure origins. This is especially important if a user agent chooses to support a Key System implementation that cannot be sufficiently sandboxed or otherwise secured. Such user agents should also properly handle Mixed Content [MIXED-CONTENT] to avoid potential exposure to insecure content. See also Use Secure Origin and Transport.

If a user agent chooses to support a Key System implementation that cannot be sufficiently sandboxed or otherwise secured, the user agent should ensure that users are fully informed and/or give explicit consent before loading or invoking it. See also User Alerts / Prompts.

7. Privacy Considerations

This section is non-normative.

The presence or use of Key Systems on a user's device raises a number of privacy issues, falling into two categories: (a) user-specific information that may be disclosed by the EME interface itself, or within messages from Key Systems and (b) user-specific information that may be persistently stored on the users device.

User Agents should take responsibility for providing users with adequate control over their own privacy. Since User Agents may integrate with third party CDM implementations, CDM implementers must provide sufficient information and controls to user agent implementers to enable them to implement appropriate techniques to ensure users have control over their privacy, including but not limited to the techniques described below.

7.1 Information Disclosed by EME and Key Systems

Concerns regarding information disclosed by EME and Key Systems fall into two categories, concerns about non-specific information that may nevertheless contribute to the possibility of fingerprinting a user agent or device and user-specific information that may be used directly for user tracking.

7.2 Fingerprinting

Malicious applications may be able to fingerprint users or user agents by detecting or enumerating the list of Key Systems that are supported and related information. If proper origin protections are not provided this could include detection of sites that have been visited and information stored for those sites. In particular, Key Systems should not share key or other data between origins.

7.3 Information Leakage

CDMs, especially those implemented outside the user agent, may not have the same fundamental isolations as the web platform. It is important that steps be taken to avoid information leakage, especially across origins. This includes both in-memory and stored data. Failure to do so could lead to information leakage to/from Incognito/Private Browsing sessions, across profiles, and even across different browsers, applications, and operating system user accounts.

To avoid such issues, user agent and CDM implementations should ensure that:

7.4 Tracking

User-specific information may be obtained over the EME API in two ways: through detection of stored keys and through Key System messages.

Key Systems may access or create persistent or semi-persistent identifiers for a device or user of a device. In some cases these identifiers may be bound to a specific device in a secure manner. If these identifiers are present in Key System messages, then devices and/or users may be tracked. If the mitigations below are not applied this could include both tracking of users / devices over time and associating multiple users of a given device. If not mitigated, such tracking may take three forms depending on the design of the Key System:

If a Key System permits keys to be stored and to be re-used between origins, then it may be possible for two origins to collude and track a unique user by recording their ability to access a common key.

Finally, if any user interface for user control of Key Systems presents data separately from data in HTTP session cookies or persistent storage, then users are likely to modify site authorization or delete data in one and not the others. This would allow sites to use the various features as redundant backup for each other, defeating a user's attempts to protect his privacy.

There are a number of techniques that can be used to mitigate these risks of tracking without user consent:

User deletion of persistent identifiers
User agents could provide users with the ability to clear any persistent identifiers maintained by Key Systems.
Use of (non-reversible) per-origin identifiers
The user / device identifier exposed by a Key System may be different for each origin, either by allocation of different identifiers for different origins or by use of a non-reversible origin-specific mapping from an origin-independent identifier.
Encryption of user identifiers
User identifiers in Key System messages could be encrypted, together with a timestamp or nonce, such that the Key System messages are always different. This would prevent the use of Key System messages for tracking except by applications fully supporting the Key System. This may be implemented using a server certificate.
Site-specific white-listing of access to each Key System
User agents could require the user to explicitly authorize access by each site to each Key System. User agents should enable users to revoke this authorization either temporarily or permanently.
Treating Key System persistent identifiers as cookies
User agents should present the presence of persistent identifiers stored by Key Systems to the user in a way that associates them strongly with HTTP session cookies. This might encourage users to view such identifiers with healthy suspicion.
Shared blacklists
User agents may allow users to share their Key System domain blacklists. This would allow communities to act together to protect their privacy.
User Alerts / Prompts
User Agents should ensure that users are fully informed and/or give explicit consent before identifiers are exposed in messages from Key Systems. Such alerts and consent should be per origin to avoid valid uses enabling subsequent malicious access. User agents that consider such alerts or consent appropriate should only support such Key Systems on secure origins (see Use Secure Origin and Transport), especially if they allow such consent to be persisted. (Granting permissions to unauthenticated origins is equivalent to granting the permissions to any origin in the presence of a network attacker.)
User controls to disable Key Systems or Key System use of identifiers
User Agents could provide users with a global control of whether a Key System is enabled / disabled and/or whether Key System use of user / device identifiers is enabled or disabled (if supported by the Key System).

While these suggestions prevent trivial use of this feature for user tracking, they do not block it altogether. Within a single domain, a site can continue to track the user during a session, and can then pass all this information to a third party along with any identifying information (names, credit card numbers, addresses) obtained by the site. If a third party cooperates with multiple sites to obtain such information, and if identifiers are not per-origin, then a profile can still be created.

It is important to note that identifiers that are non-clearable, non-origin-specific or hardware-bound exceed the tracking impact of existing techniques such as cookies or session identifiers embedded in URLs.

Thus, in addition to the various mitigations described above, if a browser supports a mode of operation intended to preserve user anonymity, then User Agent implementers should carefully consider whether access to Key Systems should be disabled in this mode.

7.5 Information Stored on User Devices

Key Systems may store information on a user's device, or user agents may store information on behalf of Key Systems. Potentially, this could reveal information about a user to another user of the same device, including potentially the origins that have used a particular Key System (i.e. sites visited) or even the content that has been decrypted using a Key System.

If information stored by one origin affects the operation of the Key System for another origin, then potentially the sites visited or content viewed by a user on one site may be revealed to another, potentially malicious, site.

There are a number of techniques that can be used to mitigate these privacy risk to users:

Origin-specific Key System storage
User agents may require that some or all of the Key System's persistently stored data is stored in an origin-specific way. Session data, licenses, and keys that are persistently stored should be stored per-origin.
User deletion of Key System storage
User agents may present the user with a way to delete Key System storage for a specific origin or all origins.
Treating Key System stored data like cookies / Web Storage
User agents should present the presence of persistent data stored by Key Systems to the user in a way that associates it strongly with HTTP session cookies and/or Web Storage. This might encourage users to view such data with healthy suspicion.
Encryption or obfuscation of Key System stored data
User agents should treat data stored by Key Systems as potentially sensitive; it is quite possible for user privacy to be compromised by the release of this information. To this end, user agents should ensure that such data is securely stored and when deleting data, it is promptly deleted from the underlying storage.

User agent and CDM implementations that allow the CDM to persist data should:

7.6 Use Secure Origin and Transport

In order to protect identifiers and other information discussed in previous sections, user agents may choose to only support the EME APIs and/or specific Key Systems (i.e. based on privacy and security risks) on secure origins. This is especially important if a user agent chooses to support a Key System implementation that exposes identifiers or other such information without effectively anonymizing it in transit (i.e. without encrypting identifiers).

Regardless of user agent limitations, applications should use secure transport (e.g. HTTPS) for all traffic containing messages from the CDM (i.e. all data passed from message events and to update()).

All user agents should properly handle Mixed Content [MIXED-CONTENT] to avoid exposure to insecure content or transport when the user agent or application wish to enforce secure origin and transport.

8. Examples

This section is non-normative.

This section contains example solutions for various use cases using the proposed extensions. These are not the only solutions to these use cases. Video elements are used in the examples, but the same would apply to all media elements. In some cases, such as using synchronous XHR, the examples are simplified to keep the focus on the extensions.

8.1 Source and Key Known at Page Load (Clear Key)

In this simple example, the source file and clear-text license are hard-coded in the page. Only one session will ever be created.

Example 3
<script>
  function load() {
    var video = document.getElementById("video");

    if (!video.mediaKeys) {
      var promise = MediaKeys.create("org.w3.clearkey");
      promise.catch(
        console.error.bind(console, "Unable to create MediaKeys")
      );
      promise.then(
        function(createdMediaKeys) {
          return video.setMediaKeys(createdMediaKeys);
        }
      ).catch(
        console.error.bind(console, "Unable to set MediaKeys")
      );
      promise.then(
        function(createdMediaKeys) {
          var initData = new Uint8Array([ ... ]);
          var keySession = createdMediaKeys.createSession();
          keySession.addEventListener("message", handleMessage, false);
          return keySession.generateRequest("webm", initData);
        }
      ).catch(
        console.error.bind(console, "Unable to create or initialize key session")
      );
    }
  }

  function handleMessage(event) {
    var keySession = event.target;

    var license = new Uint8Array([ ... ]);
    keySession.update(license).catch(
      console.error.bind(console, "update() failed")
    );
  }
</script>

<body onload="load()">
  <video src="foo.webm" autoplay id="video"></video>
</body>

8.2 Selecting a Supported Key System and Using Initialization Data from the encrypted Event

This example selects a supported Key System using the isTypeSupported() method then uses the Initialization Data from the media data to generate the license request and send it to the appropriate license server. One of the supported key systems uses a serverCertificate, which is provided proactively.

Example 4
<script>
  var keySystem;
  var licenseUrl;
  var serverCertificate;

  function selectKeySystem() {
    if (MediaKeys.isTypeSupported("com.example.somesystem", "webm", "video/webm; codecs='vp8, vorbis'")) {
      licenseUrl = "https://license.example.com/getkey"; // OR "https://example.<My Video Site domain>"
      keySystem = "com.example.somesystem";
      serverCertificate = new Uint8Array([ ... ]);
    } else if (MediaKeys.isTypeSupported("com.foobar", "webm", "video/webm; codecs='vp8, vorbis'")) {
      licenseUrl = "https://license.foobar.com/request";
      keySystem = "com.foobar";
    } else {
      throw "Key System not supported";
    }
  }

  function handleInitData(event) {
    var video = event.target;
    if (video.mediaKeysObject === undefined) {
      selectKeySystem();
      video.mediaKeysObject = null; // Prevent entering this path again.
      video.pendingSessionData = []; // Will store all initData until the MediaKeys is ready.
      MediaKeys.create(keySystem).then(
        function(createdMediaKeys) {
          video.mediaKeysObject = createdMediaKeys;

          if (serverCertificate)
            createdMediaKeys.setServerCertificate(serverCertificate);

          for (var i = 0; i < video.pendingSessionData.length; i++) {
            var data = video.pendingSessionData[i];
            makeNewRequest(video.mediaKeysObject, data.initDataType, data.initData);
          }
          video.pendingSessionData = [];

          return video.setMediaKeys(createdMediaKeys);
        }
      ).catch(
        console.error.bind(console, "Unable to create or use new MediaKeys")
      );
    }
    addSession(video, event.initDataType, event.initData);
  }

  function addSession(video, initDataType, initData) {
    if (video.mediaKeysObject) {
      makeNewRequest(video.mediaKeysObject, initDataType, initData);
    } else {
      video.pendingSessionData.push({initDataType: initDataType, initData: initData});
    }
  }

  function makeNewRequest(mediaKeys, initDataType, initData) {
    var keySession = mediaKeys.createSession();
    keySession.addEventListener("message", licenseRequestReady, false);
    keySession.generateRequest(initDataType, initData).catch(
      console.error.bind(console, "Unable to create or initialize key session")
    );
  }

  function licenseRequestReady(event) {
    var request = event.message;

    var xmlhttp = new XMLHttpRequest();
    xmlhttp.keySession = event.target;
    xmlhttp.open("POST", licenseUrl);
    xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState == 4) {
        var license = new Uint8Array(xmlhttp.response);
        xmlhttp.keySession.update(license).catch(
          console.error.bind(console, "update() failed")
        );
      }
    }
    xmlhttp.send(request);
  }
</script>

<video src="foo.webm" autoplay onencrypted="handleInitData(event)"></video>

8.3 Create MediaKeys Before Loading Media

Initialization is much simpler if encrypted events do not need to be handled during MediaKeys initialization. This can be accomplished either by providing the Initialization Data in other ways or setting the source after the MediaKeys object has been created. This example does the latter.

Example 5
<script>
  var keySystem;
  var licenseUrl;
  var serverCertificate;
  var mediaKeys;

  // See the previous example for implementations of these functions.
  function selectKeySystem() { ... }
  function makeNewRequest(mediaKeys, initDataType, initData) { ... }
  function licenseRequestReady(event) { ... }

  function handleInitData(event) {
    makeNewRequest(mediaKeys, event.initDataType, event.initData);
  }

  selectKeySystem();
  MediaKeys.create(keySystem).then(
    function(createdMediaKeys) {
      mediaKeys = createdMediaKeys;
      var video = document.getElementById("v");
      video.src = "foo.webm";
      if (serverCertificate)
        mediaKeys.setServerCertificate(serverCertificate);
      return video.setMediaKeys(mediaKeys);
    }
  ).catch(
    console.error.bind(console, "Unable to create or initialize key session")
  );
</script>

<video id="v" autoplay onencrypted="handleInitData(event)"></video>

8.4 Using All Events

This is a more complete example showing all events being used.

Note that handleMessage() could be called multiple times, including in response to the update() call if multiple round trips are required and for any other reason the Key System might need to send a message.

Example 6
<script>
  var keySystem;
  var licenseUrl;
  var serverCertificate;
  var mediaKeys;

  // See previous examples for implementations of these functions.
  // selectKeySystem() additionally sets renewalUrl.
  function selectKeySystem() { ... }
  function handleInitData(event) { ... }

  // This replaces the implementation in the previous example.
  function makeNewRequest(mediaKeys, initDataType, initData) {
    var keySession = mediaKeys.createSession();
    keySession.addEventListener("message", handleMessage, false);
    keySession.addEventListener("keyschange", handleKeysChange, false);
    keySession.closed.then(
      console.log.bind(console, "Session closed")
    );
    keySession.generateRequest(initDataType, initData).catch(
      console.error.bind(console, "Unable to create or initialize key session")
    );
  }

  function handleMessageResponse(keySession, response) {
    var license = new Uint8Array(response);
    keySession.update(license).catch(
      function(err) {
        console.error("update() failed: " + err);
      }
    );
  }

  function sendMessage(type, message, keySession) {
    var url = licenseUrl;
    if (type == "licenserenewal")
      url = renewalUrl;
    xmlhttp = new XMLHttpRequest();
    xmlhttp.keySession = keySession;
    xmlhttp.open("POST", url);
    xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState == 4)
        handleMessageResponse(xmlhttp.keySession, xmlhttp.response);
    }
    xmlhttp.send(message);
  }

  function handleMessage(event) {
    sendMessage(event.type, event.message, event.target);
  }

  function handleKeysChange(event) {
    event.target.getUsableKeyIds().then(
      function(keyIdSequence) {
        // Process keyIdSequence and respond appropriately.
      }
    ).catch(
      console.error.bind(console, "Failed handling usable keys change")
    );
  }

  function handleError(event) {
    // Report and do some bookkeeping with event.target.sessionId if necessary.
  }

  selectKeySystem();
  MediaKeys.create(keySystem).then(
    function(createdMediaKeys) {
      mediaKeys = createdMediaKeys;
      var video = document.getElementById("v");
      video.src = "foo.webm";
      if (serverCertificate)
        mediaKeys.setServerCertificate(serverCertificate);
      return video.setMediaKeys(mediaKeys);
    }
  ).catch(
    console.error.bind(console, "Unable to create or use new MediaKeys")
  );
</script>

<video id="v" autoplay onencrypted="handleInitData(event)"></video>

8.5 Stored License

This example requests a persistent license for future use and stores it. It also provides functions for later retrieving the license and for destroying it.

Example 7
<script>
  var keySystem;
  var licenseUrl;
  var serverCertificate;
  var mediaKeys;

  // See the previous examples for implementations of these functions.
  function selectKeySystem() { ... }
  function sendMessage(message, keySession) { ... }
  function handleMessage(event) { ... }
  function handleError(event) { ... }

  // Called if the application does not have a stored sessionId for the media resource.
  function makeNewRequest(mediaKeys, initDataType, initData) {
    var keySession = mediaKeys.createSession("persistent");
    keySession.addEventListener("message", handleMessage, false);
    keySession.closed.then(
      function() {
        console.log("Session " + this.sessionId + " closed");
      }.bind(keySession)        
    );
    keySession.generateRequest(initDataType, initData).then(
      function() {
        // Store this.sessionId in the application.
      }.bind(keySession)
    ).catch(
      console.error.bind(console, "Unable to create or initialize a persistable key session")
    );
  }

  // Called if the application has a stored sessionId for the media resource.
  function loadStoredSession(mediaKeys, sessionId) {
    var keySession = mediaKeys.createSession("persistent");
    keySession.addEventListener("message", handleMessage, false);
    keySession.closed.then(
      console.log.bind(console, "Session closed")
    );
    keySession.load(sessionId).then(
      function(loaded) {
        if (!loaded) {
          console.error("No stored session with the ID " + sessionId + " was found.");
          // The application should remove its record of |sessionId|.
          return;
        }
      }
    ).catch(
      console.error.bind(console, "Unable to load or initialize the stored session with the ID " + sessionId)
    );
  }

  // Called when the application wants to stop using the session without removing the stored license.
  function closeSession(keySession) {
    keySession.close();
  }

  // Called when the application wants to remove the stored license.
  // The stored session data has not been completely removed until the promise returned by remove() is fulfilled.
  // The remove() call may initiate a series of messages to/from the server that must be completed before this occurs.
  function removeStoredSession(keySession) {
    keySession.remove().then(
      function() {
        console.log("Session " + this.sessionId + " removed");
        // The application should remove its record of this.sessionId.
      }.bind(keySession)
    ).catch(
      console.error.bind(console, "Failed to remove the session")
    );
  }

  // This replaces the implementation in the previous example.
  function handleMessageResponse(keySession, response) {
    var license = new Uint8Array(response);
    keySession.update(license).then(
      function() {
        // If this was the last required message from the server, the license is
        // now stored. Update the application state as appropriate.
      }
    ).catch(
      console.error.bind(console, "update() failed")
    );
  }

  selectKeySystem();
  MediaKeys.create(keySystem).then(
    function(createdMediaKeys) {
      mediaKeys = createdMediaKeys;
      var video = document.getElementById("v");
      if (serverCertificate)
        mediaKeys.setServerCertificate(serverCertificate);
      return video.setMediaKeys(mediaKeys);
    }
  ).catch(
    console.error.bind(console, "Unable to create or use new MediaKeys")
  );
</script>

<video id="v" src="foo.webm" autoplay></video>

9. Revision History

Version Comment
10 October 2014 Converted to ReSpec.
3 September 2014 Reorganized by object.
27 August 2014 Moved license request generation and session loading to MediaKeySession.
18 August 2014 Produced candidate WD.
14 April 2014 Use promises.
1 April 2014 Moved Container Guidelines to the Encrypted Media Extensions Stream Format and Initialization Data Format Registry.
3 February 2014 Produced candidate WD.
17 September 2013 Produced candidate WD.
6 May 2013 Produced updated candidate FPWD.
14 January 2013 Produced candidate FPWD.
16 August 2012 Converted to the object-oriented API.
0.1b Last non-object-oriented revision.
0.1a Corrects minor mistakes in 0.1.
0.1 Initial Proposal
See a problem? Select text and .

A. References

A.1 Normative references

[DOM]
Anne van Kesteren; Aryeh Gregor; Ms2ger; Alex Russell; Robin Berjon. W3C DOM4. 10 July 2014. W3C Last Call Working Draft. URL: http://www.w3.org/TR/dom/
[ENCODING]
Anne van Kesteren; Joshua Bell; Addison Phillips. Encoding. 16 September 2014. W3C Candidate Recommendation. URL: http://www.w3.org/TR/encoding/
[HTML5]
Robin Berjon; Steve Faulkner; Travis Leithead; Erika Doyle Navara; Edward O'Connor; Silvia Pfeiffer. HTML5. 16 September 2014. W3C Proposed Recommendation. URL: http://www.w3.org/TR/html5/
[JWK]
Mike Jones. JSON Web Key (JWK). 28 May 2013. Internet Draft. URL: http://tools.ietf.org/html/draft-ietf-jose-json-web-key-11
[MIXED-CONTENT]
Mike West. Mixed Content. 16 September 2014. W3C Working Draft. URL: http://www.w3.org/TR/mixed-content/
[RFC6381]
R. Gellens; D. Singer; P. Frojdh. The 'Codecs' and 'Profiles' Parameters for "Bucket" Media Types. August 2011. Proposed Standard. URL: http://www.ietf.org/rfc/rfc6381.txt

A.2 Informative references

[EME-REGISTRY]
David Dorwin; Adrian Bateman; Mark Watson. Encrypted Media Extensions Stream Format and Initialization Data Format Registry. URL: initdata-format-registry.html
[JWS]
M. Jones; J. Bradley; N. Sakimura. JSON Web Signature (JWS). 25 September 2014. Internet Draft. URL: https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-33
[RFC6838]
N. Freed; J. Klensin; T. Hansen. Media Type Specifications and Registration Procedures. January 2013. Best Current Practice. URL: http://www.ietf.org/rfc/rfc6838.txt