深入理解DRM——了解Widevine与OEMCrypto
Posted zhanghui_cuc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解DRM——了解Widevine与OEMCrypto相关的知识,希望对你有一定的参考价值。
文章目录
基本术语定义
- 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 uniquely 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.
KeyBox是什么
设备上安装的keybox建立了一个root of trust,设备的secure hardware用于保护keybox的内容,keybox包含:
- 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
共128bytes。每个设备对应唯一的device id,唯一的keybox。keybox中的一些字段可以用OEMCrypto中的API获取。
举两个例子:
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,
}
};
KeyBox的烧写流程如下图
Widevine的级别
widevine分为三种级别,如下图
可以看到,level3的实现不依赖于硬件保护的key,video通路也不是加密的。整体来说加密级别比较低,所以实际上很多高质量内容(4k)必须在支持level1的设备上播放。一般也建议新设备都提供对level1的支持。
Widevine DRM Framework
大体可分为三部分:
第一部分是包含在AOSP中的内容,包括MedidaDRM,MediaCrypto,WVMExtractor等。
第二部分是Widevine的专利代码包。这部分代码需要得到Google授权后才能得到。它提供了很多Widevine专用库,用于完成Widevine DRM权限检查和解密。同时,它还提供了一些Sample App用于测试。一般在厂商源码的vendor/widevine/目录下。
第三部分是厂商自身的安全认证。Widevine可在硬件层与厂商的安全机制绑定,很多厂商也都在底层中加入自己的安全机制。一般在tee中。
Widevine Classic与Widevine Modular
Widevine Classic是一种历史上曾用的Widevine方案,现在已经被Widevine Modular完全取代。
贴出一张Widevine Classic的总体架构图供参考,如下
而android目前用的Widevine Modular的架构则如下图所示
关于两者的区别,详述如下:
Widevine Classic是Google专有的用于live、vod以及本地片源的DRM scheme,它要求媒体内容使用Google特有的封装格式(wvm)。Widevine Classic在各种设备和平台上都在逐渐被Widevine Modular所取代,因为使用DRM Plugin工作,非常依赖于widevine专有的东西,比如wvm封装格式,而且只支持单一session。
Widevine Modular是Widevine Classic的继任者,支持Common Encryption (CENC) with MPEG-DASH,使用MediaDRM工作,搭配MediaCodec和MediaCrypto API,提供更通用的加密方案,也被称作WidevineCENC, Widevine DASH,Widevine CDM(就是我们常见的WVCDM)。
自Android M起,Android设备就已经不支持Widevine Classic。如今多数的应用也只支持Widevine Modular。
补充一张对比表:
OEMCrypto是什么
很多介绍DRM的文章在讲完上面的基本概念之后就会开始介绍MediaDRM和MediaCrypto的API了,但这里我觉得可以先深入了解一下更底层的解密原理,有助于后面理解上层API的设计思路。
上图所示MediaDrm and MediaCrypto是java层的api,具体的实现在DrmEngine中,对应Widevine的称为WVDrmEngine.OEMCrypto可以认为是底层硬件和DrmEngine之间的硬件抽象层。
总体而言,OEMCrypto实现了下面四个功能:
- 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.OEMCrypto中的Session
在Widevine Classic中,最多只支持一个session,而到了Widevine Modular开始支持多Session,Session的概念也越发重要和丰富。
总体而言,一个session的上下文(context)就对应解密一个流时需要的key等各类信息。比如,解密一个流时需要一个video content key和一个audio content key,可能还需要其他一些key,这些key都保存在一个session context中,再比如,当需要切换分辨率时,就需要创建一个新的session,新的session里面保存了一组新的key。
那么一个Session里面会做什么?基本对应下面这张图
在license request时需要添加nonce和signature。
nonce是一个随机数,用于防止replay attack(重放攻击,一种网络攻击,它恶意的欺诈性的重复或拖延正常的数据传输)。服务器可以把每一次请求的Nonce保存到数据库,客户端再一次提交请求时将请求头中的Nonce与数据库中的数据作比较,如果已存在该Nonce,则证明该请求有可能是恶意的。
Signature(签名)的生成过程如下图
使用下式生成:signature = HMACSHA256(mac_key, msg)。
在LoadKeys中进行verify的流程如下图
图中的key_array元素对应OEMCrypto_KeyObject结构体。
图中的enc_key和mac_key是从keybox中的device_key派生出来的。
- 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)
对应的方法在OEMCrypto_GenerateDerivedKeys()。
Key Control
对key control block,key control block规定了每个content key所保护的流的security constraints,比如数据通路的安全要求\key的有效时间(在每次调用DecryptCTR会检查)\输出控制等等.key control block共128bit,内容如下
2. OEMCrypto API
包含五部分内容,这其中有的是专为Widevine Modular增加的,还有的则是Widevine Classic和Modular共有的。
2.1 Crypto Device Control
负责security hardware的初始化和mode control。
OEMCrypto_Open和Close方法,前者打开security engine并分配一块内存专用于crypto操作,并且如果使用hardware decryption的话,这块内存也一定是可以被decrypt hardware访问的,相应的,close方法就是关闭security engine并且释放前面分配的内存。
2.2 Crypto Key Ladder
直译为key的阶梯/梯子,其实理解为key的栈更好,我们知道所有的key在传输时都会被加密,所以想要使用一个key,必须要先对其进行解密(一般就是用位于key ladder顶端的key来解密),解密得到的key又被加到key ladder中为接下来的操作做好准备.这部分API同时要求设备必须提供AES-128 ECB,CBC和CTR的硬件支持,保证clear key不会暴露给cpu。总结而言,这部分API的作用是key managment。
这部分API在Widevine Classic和Widevine Modular中有所不同。
在Widevine Classic中:
SetEntitlementKey的作用可以用下图描述:
总体来说,就是用keybox中加密的device key来解密通过网络或其他方法传输来的EMM key(entitlement(EMM) key, aka. asset key)。
- step1:使用OEM root key解密keybox中AES 128 ECB加密的device key, 得到的clear device key 保存在hardware key ladder中。
- step2:使用上一步得到的clear device key解密AES-128-ECB加密的EMM key,得到的EMM clear key同样保存在hardware中以供下一步操作。
补充:什么是EMM和ECM?
它们其实是来自于数字电视广播中的术语。在数字电视中,通过组合扰频器、加扰和加密,数据流被48位密钥加扰,密钥被称为控制字。在一定时间内控制字的值是很小,在正常情况下,每分钟控制字会被内容提供商改变几次。控制字是通过一个方法连续生成并且不可预见;DVB规范中建议使用物理芯片生成。为了接收到解扰后的数据流,就必须实时得知当前控制字。在实践中,控制字会稍微提前收到,所以不会中断收看。加密通常用来保护控制字传输到接收者:控制字被加密为entitlement
control message(ECM,授权控制信息)。授权机构通过entitlement management
message(EMM,授权管理信息)发送授权给接收机,接收机中的CA子系统就有权解密控制字。EMM消息通过接收机的智能卡具体到每个定阅者。
DeviceControlWord的作用,同样可以用一张图来表示
可以看到正如前面所述的,利用上一个方法中得到的clear EMM key解密加密的ECM key,得到的clear ECM key的前四个字节作为flag返回,中间16字节再次保存到hardware中作为控制字用于后面的payload decrypt。
上面的流程就可以看做一个三级key ladder:
前面说完了Classic,下面来说一下Modular:
-
OEMCrypto_OpenSession
-
OEMCrypto_CloseSession
这俩略。 -
OEMCrypto_GenerateDerivedKeys
用device_key派生出mac_key和encrypt_key,前者用于和license server之间通信的签名,后者用于解密content key.如下图所示:
-
OEMCrypto_GenerateNonce
如前所述,该方法用于生成32bit的nonce以防止replay attack。 -
OEMCrypto_GenerateSignature
如前所述,利用mac_key为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;
为当前session的解密工作做准备,得到解密后的content key以及key control block信息,
如下图所示
方法会用入参中的signature验证签名,如果签名不符合则返回OEMCrypto_ERROR_SIGNATURE_FAILURE。
- OEMCrypto_RefreshKeys
为了当前session能够持续的进行解密,需要更新已有的key。
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;
Trusted memory中会维护一个key table,更新key的duration。同样,该方法也会去检查signature和nonce。
- OEMCrypto_QueryKeyControl
返回对应key的key control block(已解密的),只有当loadkeys方法成功调用时,该方法才会返回key control block(以网络字节序),否则返回OEMCrypto_ERROR_NO_CONTENT_KEY。
2.3 Decryption API
这一部分在Widevine Modular和WIdevine Classic中有比较大的不同。
在Widevine Classic中:
又被称为Video Path API,可以直译为视频通路API,即保护视频内容不会被随意解密、随意访问。有两种实现方案,不管哪种方法,都要求设备支持AES 128 CBC. ECB, CBC-CTS。
- 方案1-保护存储已解密数据的buffer,所谓hardware firewall,如下图所示
在OEMCrypto_DecryptAudio和OEMCrypto_DecryptoVideo方法中实现解密操作,video stream被解密到firewalled buffer中,音频流一般被解密到普通的buffer中。 - 方案二:在解码器中实现解密操作
在上面的方案中,解密操作在OEMCrypto API中实现,而另一种方案是把解密操作留给decoders去实现。在这种方案中也可能会用到OEMCrypto_DecryptAudio和OEMCrypto_DecryptoVideo方法,但它们的作用已经改变,也许只是给buffer加上一些metadata或header信息来告诉decoder哪些buffer是需要解密的。因为在这种方案中给player的buffer是加密的,所以就不需要firewall了。
在level2中,使用OEMCrypto_Decrypt方法解密视音频流,送给player的buffer是clear的。
上面三个方法的参数都包含了input buffer,output buffer,前两个方法的入参还包含了iv,并且利用了前面得到的clear ECM key来解密,区别是第一个方法的output buffer是secured。
下面再来看Widevine Modular:
和Widevine Classic一样,secure decode and render有两种实现方法,一种是将解密后的流送到受硬件保护的buffer中,然后把这些secured buffer送给decoder&renderer;另一种是在decoder/renderer中实现解密的操作。
-
OEMCrypto_SelectKey
使用key_id选择一个content_key(同时还包含了key control block),这个content_key供session做解密(OEMCrypto_DecryptCTR)操作用.这个key一定要是之前被LoadKeys或RefreshKeys installed过的。
值得注意的返回值
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, (每一次解密iv的后64bit加一,达到最大值后变为0,循环)
size_t block_offset, (当使用secure buffer时,存储数据可能会有一个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;
将data_buffer的数据解密(is_encrypted=true, AES-128-CTR)或直接copy(is_encrypted=false)到out_buffer中。out_buffer有三种类型:clear-即clear buffer; secure-即secure buffer;direct即直接将数据送给decoder和renderer。
返回值(仅在is_encrypted==true的时候会检查)
如果key control block中有duration参数,就会检查是否超时,否则返回KEY_EXPIRED。
如果key control block中有HDCP参数,就会检查outbuffer是否是在本地显示或者仅通过HDCP在外部显示以及HDCP version,否则返回INSUFFICIENT_HDCP。
如果key control block中有data_path_type参数,就会检查是否outbuffer是否是secure或direct的,否则返回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,甚至都不需要有session,一般用于将片头的清流copy到secure buffer中,这个过程中应用可以同时进行license request和response处理。
如果Buffer为null,返回INVALID_CONTEXT。
2.4 Keybox Provisioning
为了设备上的内容安全,需要安装widevine keybox来建立一个root of trust。这部分API就负责安装widevine keybox。
标注为optional的API可能只在OEM的factory provisioning procedure中被用到,平时不会被widevine drm plugin调用到。
自API version10开始,设备要有两个key,一个是production key,就是在工厂里安装的key,还有一个则是test keybox,test keybox在所有设备上都是相同的,只用于unit test,可以直接写死在oemcrypto libraray中。
- OEMCrypto_WrapKeybox
在制造阶段,keybox应该使用OEM root key加密并保存在文件系统中,保存的位置要确保不会在恢复出厂设置的时候被刷掉。keybox既可以一步完成加密和存储的操作,也可以分两步进行:先WrapKeybox再InstallKeybox,具体来说是在DRM plugin初始化的时候,会在/factory/wv.key中寻找wrapped keybox,然后调用OEMCrypto_IntallKeybox方法将它安装到security processor中去。
OEMCrypto_WrapKeybox方法的作用就是生成一个OEM-encrypted keybox来供OEMCrypto_IntallKeybox使用。如下图所示
- OEMCrypto_IntallKeybox
该方法的作用在前面已经描述过了,如下图
- OEMCrypto_EncryptandStoreKeybox
这个方法就是前面所说的一步到位的方法,可以看到,这一方法是在level3中用到的。
2.5 Keybox Access
顾名思义,这部分API用于keybox的读取。但是需要说明的是,在level1和level2时,只有security processor可以访问keybox中的key,而在level3中,CPU也可以访问key。
- isKeyboxValid
检查两点,一个是keybox中的magic word- kbox,第二点是校验CRC - IdentifyDevice
获得device的unique id - GetKeyData
返回keybox中的 key data - GetKeyboxData
解密keybox并返回数据
WIdevine Modular新增:
- GetRandom
返回hardware generated random bytes - API_Version
返回api version - SecurityLevel
返回当前库的security level,该方法返回的信息仅供参考 - HDCPCapability
返回设备支持的最高HDCP版本,以及当前使用的HDCP版本
HDCP_NONE,HDCP_V1,HDCP_V2,HDCP_V2_1,HDCP_V2_2,HDCP_NO_DIGITAL_OUTPUT - GetNumberOfOpenSessions 当前打开的session数
- GetMaxNumberOfSessions 允许同时打开的session数目最大值
关键解密流程
通过前面的介绍,可以看到,解密主要涉及到两个方法:
OEMCrypto_SelectKey() :used to prepare one of the previously loaded keys for decryption.
content_key拿到之后,利用OEMCrypto_DecryptCTR完成解密
关于这两个方法的介绍参见上一小节。
欢迎关注我的公众号灰度五十,分享各类音视频、移动开发知识~
文章帮到你了?可以扫描如下二维码进行打赏,打赏多少您随意~
以上是关于深入理解DRM——了解Widevine与OEMCrypto的主要内容,如果未能解决你的问题,请参考以下文章
如何在android webview中播放widevine drm内容