Posted by Amir Mazzarella on February 28, 2018
I haven't made a blog post dedicated to Widevine yet; maybe that's because I'm scared of the repercussions. Section 1201 of the DMCA states that "No person shall circumvent a technological measure that effectively controls access to a work protected under [the DMCA]." I don't agree with this law. What it's essentially saying is that someone who tampers with DRM is subject to civil and criminal penalties. Sounds fine on paper, but this law can be exploited to punish security researchers who want to find flaws that can harm the end-user. It's why I've been scared of talking about my research. However, for my own sake, I cannot confirm or deny whether or not I have "tampered" with DRM. With that out of the way, let's discuss Widevine! For the unaware, Widevine is a web streaming (both VOD and live) DRM implementation, with Google owning the company. It is based on the EME DRM standard, the standard proposed by W3C to define how HTML5 DRM systems should operate, i.e., through JavaScript which communicates with external CDMs that handle decryption. EME mandates that a CDM must first open a session, parse initialization data, formulate a license request, parse the resulting license for decryption keys, and play the encrypted video. All of this is done within its sandbox for security reasons. Widevine follows this standard, but in a way that even their message system is more "secure" than you would think. EME doesn't mandate how the CDM formats its license requests or messages, just that it has to be there. Widevine uses protocol buffers for its message syntax; you can check out the documentation for protocol buffers here. Protocol buffers are a way of serializing structured data to a string. However, there's a catch. Here's what makes the message system secure: when you serialize structured data to a string, you lose the names of the keys for each value. For instance, let's say you have this structured data:
{
init_data: ARBITRARY_BYTES,
expiry_time: 24934592932
}
When serialized, it becomes a string that is not meant to be read. When you deserialize it, it becomes this:
{
1: ARBITRARY_BYTES,
2: 24934592932
}
See? How are you supposed to know what the arbitrary bytes are? Or what that number is? The key lies in the form of a .proto
file. The proto file contains the mappings for each index to name. For example, in the case of my example, the message inside the proto file would look like this:
message Example {
optional bytes init_data = 1;
optional uint32 expiry_time = 2;
}
Without that .proto
file, you wouldn't be able to recover the names for the indices, making Widevine's message implementation relatively secure, since they don't distribute the proto. In a future blog post, I may elaborate on this concept and talk about how Widevine shot themselves in the foot and ultimately invalidated the security of protocol buffers. With exposition out of the way, I would like to discuss VMP. Before Widevine version 1.4.8.984 (included in Chrome 59) license request encryption through the use of a service certificate was optional. With the addition of VMP as a security feature, service certificates have been made mandatory. Service certificates are serialized protocol buffers that contain the RSA public key of the provider the CDM are obtaining a license for, with the blob being signed with Widevine's 3072 bit RSA key. The CDM loads the service certificate, verifies the signature to make sure the key in the service certificate has not been tampered with and encrypts the client identification blob of the license request against the RSA public key. Seems pretty secure, right? Mainly because the new security feature, VMP, includes additional information that is in the client identification blob to help verify that the CDM and chrome browser files have not been tampered with. Here's what makes it insecure, however. The CDM only verifies the signature once. The CDM verifies the service certificate signature before loading it, loads it into memory, and doesn't look at the signature again. What this means is that if you were to set a JS breakpoint when the EME JS calls a function to invoke the CDM to generate a license request, you could modify the service certificate key in memory and have the CDM encrypt the license request against the modified key. Of course, license acquisition would fail, but you would have a decryptable license request, invalidating VMP.
This blog post is getting quite long; I was initially going to talk about forward secrecy and how it would make Widevine a lot more secure, but I'll save it for a future blog post. Thanks for reading!