// Copyright 2015 Google Inc. All Rights Reserved. // Based on the EME draft spec from 2015 June 01. // https://rawgit.com/w3c/encrypted-media/1cbedad/index.html // TODO: Verify behavior and update to June 12 draft. #ifndef WVCDM_CDM_CDM_H_ #define WVCDM_CDM_CDM_H_ #if defined(_MSC_VER) typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef int int32_t; typedef __int64 int64_t; #else # include #endif #include #include // Define CDM_EXPORT to export functionality across shared library boundaries. #if defined(WIN32) # if defined(CDM_IMPLEMENTATION) # define CDM_EXPORT __declspec(dllexport) # else # define CDM_EXPORT __declspec(dllimport) # endif // defined(CDM_IMPLEMENTATION) #else // defined(WIN32) # if defined(CDM_IMPLEMENTATION) # define CDM_EXPORT __attribute__((visibility("default"))) # else # define CDM_EXPORT # endif #endif // defined(WIN32) namespace widevine { class CDM_EXPORT Cdm { public: // Session types defined by EME. typedef enum { kTemporary = 0, kPersistent = 1, } SessionType; // Message types defined by EME. typedef enum { kLicenseRequest = 0, kLicenseRenewal = 1, kLicenseRelease = 2, kIndividualizationRequest = 3, } MessageType; typedef enum { // These are defined by Widevine: kSuccess = 0, kNeedsDeviceCertificate = 1, kSessionNotFound = 2, kDecryptError = 3, kNoKey = 4, // These are analogous to the errors used by EME: kInvalidAccess = 5, kNotSupported = 6, kInvalidState = 7, kQuotaExceeded = 8, // This covers errors that we do not expect (see logs for details): kUnexpectedError = 99999, } Status; // These are the init data types defined by EME. typedef enum { kCenc = 0, kKeyIds = 1, // NOTE: not supported by Widevine at this time kWebM = 2, } InitDataType; // These are key statuses defined by EME. typedef enum { kUsable = 0, kExpired = 1, kOutputNotAllowed = 2, kStatusPending = 3, kInternalError = 4, } KeyStatus; // These are defined by Widevine. The CDM can be configured to decrypt in // three modes (dependent on OEMCrypto support). typedef enum { // Data is decrypted to an opaque handle. // Translates to OEMCrypto's OEMCrypto_BufferType_Secure. kOpaqueHandle = 0, // Decrypted data never returned to the caller, but is decoded and rendered // by OEMCrypto. // Translates to OEMCrypto's OEMCrypto_BufferType_Direct. kDirectRender = 1, // There is no secure output available, so all data is decrypted into a // clear buffer in main memory. // Translates to OEMCrypto's OEMCrypto_BufferType_Clear. kNoSecureOutput = 2, } SecureOutputType; // Logging levels defined by Widevine. // See Cdm::initialize(). typedef enum { kSilent = -1, kErrors = 0, kWarnings = 1, kInfo = 2, kDebug = 3, kVerbose = 4, } LogLevel; // A map of key statuses. // See Cdm::getKeyStatuses(). typedef std::map KeyStatusMap; // An event listener interface provided by the application and attached to // each CDM session. // See Cdm::createSession(). class IEventListener { public: // A message (license request, renewal, etc.) to be dispatched to the // application's license server. // The response, if successful, should be provided back to the CDM via a // call to Cdm::update(). virtual void onMessage(const std::string& session_id, MessageType message_type, const std::string& message) = 0; // There has been a change in the keys in the session or their status. virtual void onKeyStatusesChange(const std::string& session_id) = 0; // A remove() operation has been completed. virtual void onRemoveComplete(const std::string& session_id) = 0; protected: IEventListener() {} virtual ~IEventListener() {} }; // A storage interface provided by the application, independent of CDM // instances. // See Cdm::initialize(). // NOTE: It is important for users of your application to be able to clear // stored data. Also, browsers or other multi-application systems should // store data separately per-app or per-origin. // See http://www.w3.org/TR/encrypted-media/#privacy-storedinfo. class IStorage { public: virtual bool read(const std::string& name, std::string* data) = 0; virtual bool write(const std::string& name, const std::string& data) = 0; virtual bool exists(const std::string& name) = 0; virtual bool remove(const std::string& name) = 0; virtual int32_t size(const std::string& name) = 0; protected: IStorage() {} virtual ~IStorage() {} }; // A clock interface provided by the application, independent of CDM // instances. // See Cdm::initialize(). class IClock { public: // Returns the current time in milliseconds since 1970 UTC. virtual int64_t now() = 0; protected: IClock() {} virtual ~IClock() {} }; // A timer interface provided by the application, independent of CDM // instances. // See Cdm::initialize(). class ITimer { public: class IClient { public: // Called by ITimer when a timer expires. virtual void onTimerExpired(void* context) = 0; protected: IClient() {} virtual ~IClient() {} }; // Call |client->onTimerExpired(context)| after a delay of |delay_ms| ms. virtual void setTimeout(int64_t delay_ms, IClient* client, void* context) = 0; protected: ITimer() {} virtual ~ITimer() {} }; // Client information, provided by the application, independent of CDM // instances. // See Cdm::initialize(). // These parameters end up as client indentification in license requests. // All fields may be used by a license server proxy to drive business logic. // Some fields are required (indicated below), but please fill out as many // as make sense for your application. // No user-identifying information may be put in these fields! struct ClientInfo { // The name of the product or application, e.g. "TurtleTube" // Required. std::string product_name; // The name of the company who makes the device, e.g. "Kubrick, Inc." // Required. std::string company_name; // The name of the device, e.g. "HAL" std::string device_name; // The device model, e.g. "HAL 9000" // Required. std::string model_name; // The architecture of the device, e.g. "x86-64" std::string arch_name; // Information about the build of the browser, application, or platform into // which the CDM is integrated, e.g. "v2.71828, 2038-01-19-03:14:07" std::string build_info; }; // Device certificate request information. // The structure is passed by the application to the library in as an output // parameter to Cdm::initialize(). // All fields are filled in by the library to instruct the application to // handle device certificate requests, if needed. struct DeviceCertificateRequest { // If false, the library is ready to create and/or load sessions. // If true, a device certificate is needed first. // Sessions cannot be created or loaded until the device certificate has // been provisioned. bool needed; // If |needed| is true, this string contains the URL that must be used to // provision a device certificate. The request must be a POST. std::string url; // If |needed| is true, the response from the above-described HTTP POST // must be provided as an argument to this method. // Returns kSuccess if the provisioning was successful. // Any other return value means the provisioning failed and the CDM cannot // be used yet. Status acceptReply(const std::string& reply); }; // Initialize the CDM library and provide access to platform services. // All platform interfaces are required. // The |device_certificate_request| parameter will be filled in by // initialize(). // See documentation for DeviceCertificateRequest for more information. // Logging is controlled by |verbosity|. // Must be called and must return kSuccess before create() is called. static Status initialize( SecureOutputType secure_output_type, const ClientInfo& client_info, IStorage* storage, IClock* clock, ITimer* timer, DeviceCertificateRequest* device_certificate_request, LogLevel verbosity); // Query the CDM library version. static const char* version(); // Constructs a new CDM instance. // initialize() must be called first and must return kSuccess before a CDM // instance may be constructed. // The CDM may notify of events at any time via the provided |listener|, // which may not be NULL. // If |privacy_mode| is true, server certificates are required and will be // used to encrypt messages to the license server. // By using server certificates to encrypt communication with the license // server, device-identifying information cannot be extracted from the // license exchange process by an intermediate layer between the CDM and // the server. // This is particularly useful for browser environments, but is recommended // for use whenever possible. static Cdm* create(IEventListener* listener, bool privacy_mode); virtual ~Cdm() {} // Provides a server certificate to be used to encrypt messages to the // license server. // If |privacy_mode| was true in create() and setServerCertificate() is not // called, the CDM will attempt to provision a server certificate through // IEventListener::onMessage() with messageType == kIndividualizationRequest. // May not be called if |privacy_mode| was false. virtual Status setServerCertificate(const std::string& certificate) = 0; // Creates a new session. // Do not use this to load an existing persistent session. // If successful, the session_id is returned via |sessionId|. virtual Status createSession(SessionType session_type, std::string* session_id) = 0; // Generates a request based on the initData. // The request will be provided via a synchronous call to // IEventListener::onMessage(). // This is done so that license requests and renewals follow the same flow. virtual Status generateRequest(const std::string& session_id, InitDataType init_data_type, const std::string& init_data) = 0; // Loads an existing persisted session from storage. virtual Status load(const std::string& session_id) = 0; // Provides messages, including licenses, to the CDM. // If the message is a successful response to a release message, stored // session data will be removed for the session. virtual Status update(const std::string& session_id, const std::string& response) = 0; // The time, in milliseconds since 1970 UTC, after which the key(s) in the // session will no longer be usable to decrypt media data, or -1 if no such // time exists. virtual Status getExpiration(const std::string& session_id, int64_t* expiration) = 0; // A map of known key IDs to the current status of the associated key. virtual Status getKeyStatuses(const std::string& session_id, KeyStatusMap* key_statuses) = 0; // Indicates that the application no longer needs the session and the CDM // should release any resources associated with it and close it. // Does not generate release messages for persistent sessions. // Does not remove stored session data for persistent sessions. virtual Status close(const std::string& session_id) = 0; // Removes stored session data associated with the session. // The session must be loaded before it can be removed. // Generates release messages, which must be delivered to the license server. // A reply from the license server must be provided via update() before the // session is fully removed. virtual Status remove(const std::string& session_id) = 0; struct InputBuffer { public: InputBuffer() : key_id(NULL), key_id_length(0), iv(NULL), iv_length(0), data(NULL), data_length(0), block_offset(0), is_encrypted(true), is_video(true), first_subsample(true), last_subsample(true) {} const uint8_t* key_id; uint32_t key_id_length; // The IV is expected to be 16 bytes. const uint8_t* iv; uint32_t iv_length; const uint8_t* data; uint32_t data_length; // |data|'s offset within its 16-byte AES block, used for CENC subsamples. // Should start at 0 for each sample, then go up by |data_length| (mod 16) // after the |is_encrypted| part of each subsample. uint32_t block_offset; // If false, copies the input data directly to the output buffer. Used for // secure output types, where the output buffer cannot be directly accessed // above the CDM. bool is_encrypted; // Used by secure output type kDirectRender, where the secure hardware must // decode and render the decrypted content: bool is_video; bool first_subsample; bool last_subsample; }; struct OutputBuffer { OutputBuffer() : data(NULL), data_length(0), data_offset(0), is_secure(false) {} // If |is_secure| is false or the secure output type is kNoSecureOutput, // this is a memory address in main memory. // If |is_secure| is true and the secure output type is kOpaqueHandle, // this is an opaque handle. // If |is_secure| is true and the secure output type is kDirectRender, // this is ignored. // See also SecureOutputType argument to initialize(). uint8_t* data; // The maximum amount of data that can be decrypted to the buffer in this // call, starting from |data|. // Must be at least as large as the input buffer's |data_length|. // This size accounts for the bytes that will be skipped by |data_offset|. uint32_t data_length; // An offset applied to the output address. // Useful when |data| is an opaque handle rather than an address. uint32_t data_offset; // False for clear buffers, true otherwise. // Must be false if the secure output type is kNoSecureOutput. // See also SecureOutputType argument to initialize(). bool is_secure; }; // Decrypt the input as described by |input| and pass the output as described // in |output|. virtual Status decrypt(const InputBuffer& input, const OutputBuffer& output) = 0; protected: Cdm() {} }; } // namespace widevine #endif // WVCDM_CDM_CDM_H_