Definitions of Basic terms
- Device Id — A nullterminated Cstring uniquely identifying the device. 32 character maximum, including NULL termination.
- Device Key — 128bit AES key assigned by Widevine and used to secure entitlements.
- Keybox — Widevine structure containing keys and other information used to establish a root of trust on a device. The keybox is either installed during manufacture or in the field. Factory provisioned devices have a higher level of security and may be approved for access to higher quality content.
- Provision – Install a Keybox that has been redundant constructed for a specific device.
- Trusted Execution Environment (TEE) — The portion of The device that contains security hardware and prevents access by non secure system resources.
What is KeyBox
The secure Hardware of the device is used to protect the contents of the keybox. The keybox contains:
- Device ID (32bytes) : C character string identifying the Device, null terminated.
- Device Key (16bytes) : 128 bit AES Key assigned to Device, generated by Widevine.
- Key Data (72bytes) : Encrypted Data
- Magic (4bytes) : Constant code used to recognize a valid keybox: “kbox” (0x6B626f78)
- CRC (4bytes) : crc-32 posix-1003.2 validates integrity of the key data field
A total of 128 bytes. Each device has a unique device ID, a unique Keybox. Some fields in the keybox can be retrieved using the API in OEMCrypto. Two examples:
const WidevineKeybox kTestKeybox = {
// Sample keybox used for test vectors
{
// deviceID
0x54.0x65.0x73.0x74.0x4b.0x65.0x79.0x30.// TestKey01
0x31.0x00.0x00.0x00.0x00.0x00.0x00.0x00./ /...
0x00.0x00.0x00.0x00.0x00.0x00.0x00.0x00./ /...
0x00.0x00.0x00.0x00.0x00.0x00.0x00.0x00./ /...
}, {
// key
0xfb.0xda.0x04.0x89.0xa1.0x58.0x16.0x0e.0xa4.0x02.0xe9.0x29.0xe3.0xb6.0x8f.0x04}, {// data
0x00.0x00.0x00.0x02.0x00.0x00.0x10.0x19.0x07.0xd9.0xff.0xde.0x13.0xaa.0x95.0xc1.0x22.0x67.0x80.0x53.0x36.0x21.0x36.0xbd.0xf8.0x40.0x8f.0x82.0x76.0xe4.0xc2.0xd8.0x7e.0xc5.0x2b.0x61.0xaa.0x1b.0x9f.0x64.0x6e.0x58.0x73.0x49.0x30.0xac.0xeb.0xe8.0x99.0xb3.0xe4.0x64.0x18.0x9a.0x14.0xa8.0x72.0x02.0xfb.0x02.0x57.0x4e.0x70.0x64.0x0b.0xd2.0x2e.0xf4.0x4b.0x2d.0x7e.0x39}, {// magic
0x6b.0x62.0x6f.0x78}, {// Crc
0x0a.0x7a.0x2c.0x35,}};Copy the code
The KeyBox burn process is shown below
The level of Widevine
Widevine comes in three levels, as shown belowAs you can see, level3’s implementation does not rely on hardware-protected keys, and the video channel is not encrypted. The overall encryption level is low, so a lot of high-quality content (4K) actually has to be played on level1-enabled devices. It is also recommended that all new devices provide level1 support.
Widevine DRM Framework
It can be divided into three parts: The first part is the content contained in AOSP, including MedidaDRM, MediaCrypto, WVMExtractor, etc. The second part is the Widevine patent code package. This part of the code needs to be authorized by Google. It provides a number of Widevine libraries for Widevine DRM permission checking and decryption. It also provides some Sample Apps for testing. Usually in the vendor/widevine/ directory of the vendor source code. The third part is the manufacturer’s own safety certification. Widevine can be tied to vendor security at the hardware level, and many vendors have built in their own security at the bottom. Usually in tee.
Widevine Classic with Widevine Modular
Widevine Classic is a historically used Widevine scheme that has been completely replaced by Widevine Modular.
Here is a general architecture diagram of Widevine Classic for your referenceAndroid’s current Widevine Modular architecture is as shown belowThe differences are detailed below: Widevine Classic is Google’s proprietary DRM scheme for Live, VOD, and local sources, which requires media content to use a Google-specific encapsulation format (WVM). Widevine Classic is gradually being replaced by Widevine Modular modules on all devices and platforms, because it works with the DRM Plugin, relies heavily on Widevine proprietary things like WVM encapsulation formats, and supports only a single session.
Widevine Modular is the successor to Widevine Classic, supporting Common Encryption (CENC) with MPEG-Dash, using Media Modular, With MediaCodec and MediaCrypto apis, it provides a more general encryption solution, also known as WidevineC, Widevine DASH, and Widevine CDM (commonly known as WVCDM).
Widevine Classic has not been supported on Android devices since Android M. Most apps today support only Widevine Modular as well.
Add a comparison table:
What is OEMCrypto
Many DRM articles will introduce MediaDRM and MediaCrypto apis after covering the basic concepts above, but I thought it would be helpful to take a closer look at the underlying decryption principles to understand the design of the upper API.
As shown in the figure above, MediaDrm and MediaCrypto are Java layer APIS. The specific implementation is in DrmEngine, corresponding to Widevine called WVDrmEngine. OEMCrypto can be considered as the hardware abstraction layer between the underlying hardware and DrmEngine.
In general, OEMCrypto implements the following four functions:
- verify the authenticity of messages to and from a license server
- establish a key encryption key that can be used to decrypt the key material contained in the messages (derived keys from device key)
- load encrypted content keys into the trusted environment and decrypt them
- use the content keys to produce a decrypted stream for decoding and rendering
1. The OEMCrypto Session
In Widevine Classic, a single session was supported at most, and as Widevine Modular Modular began to support multiple sessions, the concept of sessions became increasingly important and rich.
Generally speaking, the context of a session corresponds to various information such as keys needed to decrypt a stream. For example, when you decrypt a stream you need a video content key and an Audio Content key, and maybe a few other keys, all of which are stored in a session context, or when you want to switch resolutions, You need to create a new session that holds a new set of keys.
So what happens in a Session? It basically corresponds to this graphNonce and signature must be added during license request. A nonce is a random number used to prevent replay attacks (malicious and fraudulent attempts to duplicate or delay normal data transfers). The server can save the Nonce of each request to the database. When the client submits the request again, the Nonce in the request header is compared with the data in the database. If the Nonce exists, the request may be malicious.
The following figure shows the generation process of Signature
Generate with: signature = HMACSHA256(mac_key, MSG).
The process for verify in LoadKeys is shown belowThe key_array element in the figure corresponds to the OEMCrypto_KeyObject structure. Enc_key and mac_key in the figure are derived from device_key in keybox.
- encrypt_key: used to encrypt the content key:
encrypt_key := AES128CMAC(device_key, 0x01 || context_enc)
- mac_keys: used as the hash key for the HMAC to sign and verify license messages:
mac_key[server] || mac_key[client] := AES128CMAC(device_key, 0x01 || context_mac) || AES128CMAC(device_key, 0x02 || context_mac) || AES128CMAC(device_key, 0x03 || context_mac) || AES128CMAC(device_key, 0x04 || context_mac)
The corresponding method is in OEMCrypto_GenerateDerivedKeys().
Key Control specifies the Security constraints to the stream protected by each content Key. For example, the security of the data path requires the valid time of \ key (which is checked every time DecryptCTR is called) \ output control, and so on. The key control block contains 128 bits
2. OEMCrypto API
It contains five sections, some of which are dedicated to Widevine Modular additions, and others that are common to Both Widevine Classic and Modular.
2.1 Crypto Device Control
Responsible for initialization and mode control of Security Hardware.The OEMCrypto_Open and Close methods, the former turns on the Security engine and allocates a block of memory dedicated to crypto operations, which must also be accessible by Decrypt Hardware if decryption is used. Accordingly, the close method closes the Security Engine and frees the previously allocated memory.
2.2 Crypto Key Ladder
We know that all keys are encrypted during transmission, so to use a key, we must first decrypt it (usually the key at the top of the key ladder). The decrypted keys are then added to the key ladder for subsequent operations. This part of the API also requires that the device provide AES-128 ECB, CBC, and CTR hardware support to ensure that clear Key is not exposed to the CPU. In summary, this part of the API is for key managment.
This part of the API is different in Widevine Classic and Widevine Modular.
In Widevine Classic:The role of SetEntitlementKey can be described as follows:In general, the device key encrypted in the keybox is used to decrypt the EMM key(aka. Asset key) transmitted through the network or other methods.
- Step1: use OEM root key to decrypt AES 128 ecb-encrypted device key in keybox. The obtained clear device key is stored in the hardware key ladder.
- Step2: use the clear device key obtained in the previous step to decrypt the aes-128-ecb encrypted EMM key. The obtained EMM clear key is also saved in hardware for further operations.
Ps: What are EMM and ECM? They actually come from terms used in digital television broadcasting. In digital TELEVISION, the data stream is perturbed by a 48-bit key, called a control word, through a combination of scrambler, scrambler, and encryption. The value of the control word is small for a certain amount of time, and under normal circumstances the control word is changed several times per minute by the content provider. Control words are generated continuously and unpredictably by a method; Physical chip generation is recommended in the DVB specification. In order to receive the unscrambled data stream, the current control word must be known in real time. In practice, control words are received slightly ahead of time, so there is no interruption of viewing. Encryption is usually used to protect the transmission of the control word to the recipient: the control word is encrypted as the ECM (Entitlement control message). The CA subsystem of the receiver has the right to decrypt the control word by sending the authorization to the receiver through the EMM. EMM messages are specific to each subscriber via the receiver’s smart card.
The function of DeviceControlWord can also be represented by a diagram
As you can see above, decrypt the encrypted ECM key using the clear EMM key obtained in the previous method. The first four bytes of the resulting Clear ECM key are returned as a flag. The middle 16 bytes are saved again in hardware as a control word for payload Decrypt.
The process above can be viewed as a three-level key ladder:
Having said Classic, Modular:
- OEMCrypto_OpenSession
- OEMCrypto_CloseSession
Both of the just.
- OEMCrypto_GenerateDerivedKeys
The mac_key and encrypt_key are derived from Device_key. The former is used to sign communication with the license server, and the latter is used to decrypt the Content key. As shown below:
- OEMCrypto_GenerateNonce
As mentioned earlier, this method is used to generate 32bit Nonce to prevent replay attacks.
- OEMCrypto_GenerateSignature
As described earlier, use mac_key to sign the license request.
- OEMCrypto_LoadKeys
OEMCryptoResult OEMCrypto_LoadKeys(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length,
const uint8_t* enc_mac_keys_iv,
const uint8_t* enc_mac_keys,
size_t num_keys,
const OEMCrypto_KeyObject* key_array,
const uint8_t* pst,
size_t pst_length);
typedef struct {
const uint8_t* key_id;
size_t key_id_length;
const uint8_t* key_data_iv;
const uint8_t* key_data;
size_t key_data_length;
const uint8_t* key_control_iv;
const uint8_t* key_control;
} OEMCrypto_KeyObject;
Copy the code
To prepare for the decryption of the current session, obtain the decrypted Content key and key Control block information, as shown in the following figureMethod verifies the signature with the signature in the parameter and returns OEMCrypto_ERROR_SIGNATURE_FAILURE if the signature does not match.
- OEMCrypto_RefreshKeys
In order for the current session to be continuously decrypted, the existing key needs to be updated.
OEMCryptoResult OEMCrypto_RefreshKeys(OEMCrypto_SESSION session,
const uint8_t* message,
size_t message_length,
const uint8_t* signature,
size_t signature_length,
size_t num_keys,
const OEMCrypto_KeyRefreshObject* key_array);
typedef struct {
const uint8_t* key_id;
size_t key_id_length;
const uint8_t* key_control_iv;
const uint8_t* key_control;
} OEMCrypto_KeyRefreshObject;
Copy the code
A key table is maintained in Trusted Memory, updating the duration of the key. This method also checks for signature and Nonce.
- OEMCrypto_QueryKeyControl
Returns the key Control block(decrypted) for the corresponding key, which returns the key Control block(in network byte order) only if the loadKeys method is successfully called, otherwise OEMCrypto_ERROR_NO_CONTENT_KEY.
2.3 Decryption apis
This part is quite different between Widevine Modular and Widevine Classic.
In Widevine Classic: also known as the Video Path API, can literally be translated as the Video Path API, which is to protect Video content from being decrypted and accessed at will. There are two implementations, and either way requires the device to support AES 128 CBC.ECB, CBC-CTS.
- Solution 1 protects the buffer that stores the decrypted data, called the Hardware Firewall, as shown in the following figure
In the OEMCrypto_DecryptAudio and OEMCrypto_DecryptoVideo methods, the video stream is decrypted into firewalled Buffer, and the audio stream is generally decrypted into a normal buffer.
- Scheme two: decryption operation in decoder
In the above scenario, decryption is implemented in the OEMCrypto API, while in the other scenario, decryption is left to the decoders. The OEMCrypto_DecryptAudio and OEMCrypto_DecryptoVideo methods may also be used in this scheme, but their role has changed, Maybe it’s just adding metadata or headers to the buffer to tell the decoder which buffers need to be decrypted. Since the buffer given to the Player is encrypted in this scenario, there is no need for a firewall.At level2, using the OEMCrypto_Decrypt method to decrypt the video and audio stream, the buffer sent to the player is clear. Each of the above three methods has an input buffer and an output buffer, and the first two methods also have an input parameter iv, which is decrypted using the clear ECM key. The difference is that the output buffer of the first method is secured.
Widevine Modular:
As with Widevine Classic, secure decode and render can be implemented in two ways: one is to send decrypted streams to hardware-protected buffers that are then sent to decoder&renderer; The other is to implement decryption in decoder/renderer.
- OEMCrypto_SelectKey
Use key_id to select a content_key (which also contains a key control block) that is used by session to perform OEMCrypto_DecryptCTR. This key must have been previously installed by LoadKeys or RefreshKeys. The return value of note
OEMCrypto_ERROR_NO_DEVICE_KEY: Failed to decrypt device key
OEMCrypto_ERROR_NO_CONTENT_KEY: Failed to decrypt content key
OEMCrypto_ERROR_CONTROL_INVALID: invalid or unsupported control input
OEMCrypto_ERROR_KEYBOX_INVALID: cannot decrypt and read from Keybox
- OEMCrypto_DecryptCTR
OEMCryptoResult
OEMCrypto_DecryptCTR(OEMCrypto_SESSION session,
const uint8_t *data_addr,
size_t data_length,
bool is_encrypted,
const uint8_t*iv, (after each decryption of iv64bIt plus one, when it reaches its maximum value, becomes0, the loop)size_tBlock_offset, (when using Secure Buffer, stored data may have an offset)const OEMCrypto_DestBufferDesc* out_buffer,
unit8_t subsample_flags);
typedef enum OEMCryptoBufferType {
OEMCrypto_BufferType_Clear,
OEMCrypto_BufferType_Secure,
OEMCrypto_BufferType_Direct
} OEMCrytoBufferType;
typedef struct {
OEMCryptoBufferType type;
union {
struct {
uint8_t* address; size_t max_length;
} clear;
struct {
void* handle; size_t max_length; size_t offset;
} secure;
struct {
bool is_video;
} direct;
} buffer;
} OEMCrypto_DestBufferDesc;
Copy the code
Decrypt the data_buffer data (is_encrypted= true, AES-128-CTR) or copy it directly (is_encrypted=false) to out_buffer. There are three types of out_buffer: clear- clear buffer; Secure – Secure buffer; Direct means to send data directly to the decoder and renderer.
Return value (checked only if is_encrypted==true) Checks if duration is present in the key Control block, otherwise returns KEY_EXPIRED. If there is an HDCP parameter in the key Control Block, we check whether outbuffer is displayed locally or externally only with HDCP and HDCP version. Otherwise, INSUFFICIENT_HDCP is returned. If the key Control block has a data_path_type parameter, it checks to see if the outbuffer is secure or direct, otherwise it returns DECRYPT_FAIL.
- OEMCrypto_CopyBuffer
OEMCryptoResult
OEMCrypto_CopyBuffer(const uint8_t *data_addr,
size_t data_length,
const OEMCrypto_DestBufferDesc* out_buffer, unit8_t subsample_flags);
Copy the code
Simple copy, which does not even require a session, is generally used to copy the clean stream of the header to secure Buffer. In this process, the application can process license request and response simultaneously.
If Buffer is null, INVALID_CONTEXT is returned.
2.4 Keybox Provisioning
To secure content on the device, install the Widevine KeyBox to set up a root of Trust. This section of the API installs the Widevine KeyBox.The API labeled optional may only be used in OEM Factory Provisioning Procedure and is not normally invoked by the Widevine DRM Plugin.
Since API version10, the device must have two keys, one is the production key, which is the key installed in the factory, and the other is the test keybox. The test keybox is the same on all devices, and is only used for Unit test. Oemcrypto Libraray can be written to death.
- OEMCrypto_WrapKeybox
During manufacturing, keyboxes should be encrypted with the OEM root key and stored on the file system in a location that will not be erased when factory Settings are restored. Keybox can encrypt and store data in one step or in two steps: WrapKeybox and then InstallKeybox, specifically during DRM plugin initialization, Wrapped Keybox is searched in /factory/wv.key and installed into the Security processor by calling the OEMCrypto_IntallKeybox method. The OEMCrypto_WrapKeybox method is used to generate an OEMCrypto_IntallKeybox OEMCrypto_IntallKeybox. As shown in the figure below
- OEMCrypto_IntallKeybox
The action of this method has been described earlier, as shown below
- OEMCrypto_EncryptandStoreKeybox
This method is the one-step method described earlier, and as you can see, this method is used in level3.
2.5 Keybox Access
As the name suggests, this part of the API is used to read keyboxes. Note, however, that only the Security processor has access to the key in the keybox at Level1 and Level2, and the CPU has access to the key at Level3.
- isKeyboxValid
Check two points, one is magic word-kbox in keybox, the second is CRC check
- IdentifyDevice
Get the unique ID of the device
- GetKeyData
Returns the key data in the keybox
- GetKeyboxData
Decrypt the Keybox and return the data
WIdevine Modular added:
- GetRandom
Return hardware generated random Bytes
- API_Version
Returns the API version
- SecurityLevel
Returns the security level of the current library. This method returns information for reference only
- HDCPCapability
Returns the highest HDCP version supported by the device and the current HDCP versions HDCP_NONE, HDCP_V1, HDCP_V2, HDCP_V2_1, HDCP_V2_2, HDCP_NO_DIGITAL_OUTPUT
- GetNumberOfOpenSessions Number of open sessions
- GetMaxNumberOfSessions Maximum number of sessions that can be opened simultaneously
Critical decryption process
OEMCrypto_SelectKey() : used to prepare one of the previously loaded keys for decryption.
Once the content_key is obtained, it is decrypted using OEMCrypto_DecryptCTR
See the previous section for an introduction to these two methods.