Initial commit
This commit is contained in:
parent
693887386c
commit
e9757b2fd5
|
@ -0,0 +1,32 @@
|
|||
# Widevine L3 Decryptor
|
||||
[Widevine](https://www.widevine.com/solutions/widevine-drm) is a Google-Owned DRM system that's in use by many popular streaming services (Netflix, Spotify, ...) to protect media contnet from being downloaded.
|
||||
|
||||
But Widevine's least secure security level, L3, as used in most browsers and PCs, is implemented 100% in software (i.e no hardware TEEs), thereby making it reversible and bypassable.
|
||||
|
||||
This Chrome extension demonstates how it's possible to bypass Widevine DRM by hijacking calls to the browser's [Encrypted Media Extensions (EME)](https://www.html5rocks.com/en/tutorials/eme/basics/) and decrypting all Widevine content keys transferred - effectively turning it into a clearkey DRM.
|
||||
|
||||
## Usage
|
||||
To see this concept in action, just load the extnesion in developer mode and browse to any website that plays Widevine-protected content, such as https://bitmovin.com/demos/drm.
|
||||
|
||||
Keys will be logged in plaintext to the javascript console:
|
||||
|
||||
`WidevineDecryptor: Found key: 100b6c20940f779a4589152b57d2dacb (KID=eb676abbcb345e96bbcf616630f1a3da)
|
||||
`
|
||||
|
||||
Decrypting the media itself is then just a matter of using a tool that can decrypt MPEG-CENC streams, like `ffmpeg`. e.g:
|
||||
|
||||
`ffmpeg -decryption_key 100b6c20940f779a4589152b57d2dacb -i encrypted_media.mp4 -codec copy decrypted_media.mp4`
|
||||
|
||||
## How
|
||||
In the context of browsers the actual decryption of the media is usually done inside a proprietary binary (`widevinecdm.dll`, known as the Content Decryption Module or CDM) only after receiving the license from a license server with an encrypted key in it.
|
||||
|
||||
This binary is usually heavily obfuscated and makes use of third-party solutions that claim to offer software "protection" such as [Arxan](https://digital.ai/application-protection) or [Whitecryption](https://www.intertrust.com/products/application-shielding/).
|
||||
|
||||
Some reversing job on that binary can then be done to extract the secret keys and mimic the key decryption algorithm from the license response.
|
||||
|
||||
## Why
|
||||
This PoC was done to further show that code obfuscation, anti-debugging tricks, whitebox cryptography algorithms and other methods of security-by-obscurity will eventually by defeated anyway, and are, in a way, pointless.
|
||||
|
||||
## Legal Desclaimer
|
||||
This is for educational purposes only. Downloading copyrighted matirials from streaming services may violate their Terms Of Service. Use at your own risk.
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
This is where the magic happens
|
||||
*/
|
||||
|
||||
|
||||
var WidevineCrypto = {};
|
||||
|
||||
(function() {
|
||||
|
||||
// The public 2048-bit RSA key Widevine uses for Chrome devices in L3
|
||||
WidevineCrypto.chromeRSAPublicKey =
|
||||
`-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtdHcRBiDWWxdJyKDLTPO9OTapumVnW+9g6k3RSflM0CESFEufZUJGC73UKe9e+u789HVZT04pB5or3WB0XOx
|
||||
aOibJklLBkd7Yfn1OndVrenMKTE1F4/6jg5rmwyv4qFQ1u8M/ThZUrAgb8pTmKfb9vrv1V8AApwVzcQg3s48eESnKjBU99Vk8alPTjPSfOgoTDluGxQONWiwCaMwftNs
|
||||
YrOzlde+V3UOb5FVzPcrOmaERfyujV3h4sHGRbTCsqYVwMalO7hmNmtemwt0xBuf5Juia7t1scuJypQ8lI1iEsB+JZVo3Uovfa9nNX0gl5TAq1tAh6M55/ttpWAirWHv
|
||||
CQIDAQAB
|
||||
-----END PUBLIC KEY-----`;
|
||||
|
||||
// The private 2048-bit RSA key Widevine uses for authenticating Chrome devices in L3
|
||||
// Extracted by applying some mathematical tricks to Araxan's white-box algorithm
|
||||
WidevineCrypto.chromeRSAPrivateKey =
|
||||
`-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC10dxEGINZbF0nIoMtM8705Nqm6ZWdb72DqTdFJ+UzQIRIUS59lQkYLvdQp71767vz0dVlPTikHmiv
|
||||
dYHRc7Fo6JsmSUsGR3th+fU6d1Wt6cwpMTUXj/qODmubDK/ioVDW7wz9OFlSsCBvylOYp9v2+u/VXwACnBXNxCDezjx4RKcqMFT31WTxqU9OM9J86ChMOW4bFA41aLAJ
|
||||
ozB+02xis7OV175XdQ5vkVXM9ys6ZoRF/K6NXeHiwcZFtMKyphXAxqU7uGY2a16bC3TEG5/km6Jru3Wxy4nKlDyUjWISwH4llWjdSi99r2c1fSCXlMCrW0CHoznn+22l
|
||||
YCKtYe8JAgMBAAECggEAGOPDJvFCHd43PFG9qlTyylR/2CSWzigLRfhGsClfd24oDaxLVHav+YcIZRqpVkr1flGlyEeittjQ1OAdptoTGbzp7EpRQmlLqyRoHRpT+MxO
|
||||
Hf91+KVFk+fGdEG+3CPgKKQt34Y0uByTPCpy2i10b7F3Xnq0Sicq1vG33DhYT9A/DRIjYr8Y0AVovq0VDjWqA1FW5OO9p7vky6e+PDMjSHucQ+uaLzVZSc7vWOh0tH5M
|
||||
0GVk17YpBiB/iTpw4zBUIcaneQX3eaIfSCDHK0SCD6IRF7kl+uORzvWqiWlGzpdG2B96uyP4hd3WoPcZntM79PKm4dAotdgmalbueFJfpwKBgQDUy0EyA9Fq0aPF4LID
|
||||
HqDPduIm4hEAZf6sQLd8Fe6ywM4p9KOEVx7YPaFxQHFSgIiWXswildPJl8Cg5cM2EyMU1tdn5xaR4VIDk8e2JEDfhPtaWskpJp2rU2wHvAXOeAES7UFMrkhKVqqVOdbo
|
||||
IhlLdcYp5KxiJ3mwINSSO94ShwKBgQDavJvF+c8AINfCaMocUX0knXz+xCwdP430GoPQCHa1rUj5bZ3qn3XMwSWa57J4x3pVhYmgJv4jpEK+LBULFezNLV5N4C7vH63a
|
||||
Zo4OF7IUedFBS5B508yAq7RiPhN2VOC8LRdDh5oqnFufjafF82y9d+/czCrVIG43D+KO2j4F7wKBgDg/HZWF0tYEYeDNGuCeOO19xBt5B/tt+lo3pQhkl7qiIhyO8KXr
|
||||
jVilOcZAvXOMTA5LMnQ13ExeE2m0MdxaRJyeiUOKnrmisFYHuvNXM9qhQPtKIgABmA2QOG728SX5LHd/RRJqwur7a42UQ00Krlr235F1Q2eSfaTjmKyqrHGDAoGAOTrd
|
||||
2ueoZFUzfnciYlRj1L+r45B6JlDpmDOTx0tfm9sx26j1h1yfWqoyZ5w1kupGNLgSsSdimPqyR8WK3/KlmW1EXkXIoeH8/8aTZlaGzlqtCFN4ApgKyqOiN44cU3qTrkhx
|
||||
7MY+7OUqB83tVpqBGfWWeYOltUud6qQqV8v8LFsCgYEAnOq+Ls83CaHIWCjpVfiWC+R7mqW+ql1OGtoaajtA4AzhXzX8HIXpYjupPBlXlQ1FFfPem6jwa1UTZf8CpIb8
|
||||
pPULAN9ZRrxG8V+bvkZWVREPTZj7xPCwPaZHNKoAmi3Dbv7S5SEYDbBX/NyPCLE4sj/AgTPbUsUtaiw5TvrPsFE=
|
||||
-----END PRIVATE KEY-----`;
|
||||
|
||||
WidevineCrypto.initializeKeys = async function()
|
||||
{
|
||||
// load the device RSA keys for various purposes
|
||||
this.publicKeyEncrypt = await crypto.subtle.importKey('spki', PEM2Binary(this.chromeRSAPublicKey), {name: 'RSA-OAEP', hash: { name: 'SHA-1' },}, true, ['encrypt']);
|
||||
this.publicKeyVerify = await crypto.subtle.importKey('spki', PEM2Binary(this.chromeRSAPublicKey), {name: 'RSA-PSS', hash: { name: 'SHA-1' },}, true, ['verify']);
|
||||
this.privateKeyDecrypt = await crypto.subtle.importKey('pkcs8', PEM2Binary(this.chromeRSAPrivateKey), {name: 'RSA-OAEP', hash: { name: 'SHA-1' },}, true, ['decrypt']);
|
||||
|
||||
var isRSAGood = await isRSAConsistent(this.publicKeyEncrypt, this.privateKeyDecrypt);
|
||||
if (!isRSAGood)
|
||||
{
|
||||
throw "Can't verify RSA keys consistency; This means the public key does not match the private key!";
|
||||
}
|
||||
|
||||
this.keysInitialized = true;
|
||||
}
|
||||
|
||||
WidevineCrypto.decryptContentKey = async function(licenseRequest, licenseResponse)
|
||||
{
|
||||
licenseRequest = SignedMessage.read(new Pbf(licenseRequest));
|
||||
licenseResponse = SignedMessage.read(new Pbf(licenseResponse));
|
||||
|
||||
if (licenseRequest.type != SignedMessage.MessageType.LICENSE_REQUEST.value) return;
|
||||
|
||||
license = License.read(new Pbf(licenseResponse.msg));
|
||||
|
||||
if (!this.keysInitialized) await this.initializeKeys();
|
||||
|
||||
// make sure the signature in the license request validates under the private key
|
||||
var signatureVerified = await window.crypto.subtle.verify({name: "RSA-PSS", saltLength: 20,}, this.publicKeyVerify,
|
||||
licenseRequest.signature, licenseRequest.msg)
|
||||
if (!signatureVerified)
|
||||
{
|
||||
console.log("Can't verify license request signature; either the platform is wrong or the key has changed!");
|
||||
return null;
|
||||
}
|
||||
|
||||
// decrypt the session key
|
||||
var sessionKey = await crypto.subtle.decrypt({name: "RSA-OAEP"}, this.privateKeyDecrypt, licenseResponse.session_key);
|
||||
|
||||
// calculate context_enc
|
||||
var encoder = new TextEncoder();
|
||||
var keySize = 128;
|
||||
var context_enc = concatBuffers([[0x01], encoder.encode("ENCRYPTION"), [0x00], licenseRequest.msg, intToBuffer(keySize)]);
|
||||
|
||||
// calculate encrypt_key using CMAC
|
||||
var encryptKey = wordToByteArray(
|
||||
CryptoJS.CMAC(arrayToWordArray(new Uint8Array(sessionKey)),
|
||||
arrayToWordArray(new Uint8Array(context_enc))).words);
|
||||
|
||||
// iterate the keys we got to find those we want to decrypt (the content key(s))
|
||||
var contentKeys = []
|
||||
for (currentKey of license.key)
|
||||
{
|
||||
if (currentKey.type != License.KeyContainer.KeyType.CONTENT.value) continue;
|
||||
|
||||
var keyId = currentKey.id;
|
||||
var keyData = currentKey.key.slice(0, 16);
|
||||
var keyIv = currentKey.iv.slice(0, 16);
|
||||
|
||||
// finally decrypt the content key
|
||||
var decryptedKey = wordToByteArray(
|
||||
CryptoJS.AES.decrypt({ ciphertext: arrayToWordArray(keyData) }, arrayToWordArray(encryptKey), { iv: arrayToWordArray(keyIv) }).words);
|
||||
|
||||
contentKeys.push(decryptedKey);
|
||||
console.log("WidevineDecryptor: Found key: " + toHexString(decryptedKey) + " (KID=" + toHexString(keyId) + ")");
|
||||
}
|
||||
|
||||
return contentKeys[0];
|
||||
}
|
||||
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
|
||||
async function isRSAConsistent(publicKey, privateKey)
|
||||
{
|
||||
// See if the data is correctly decrypted after encryption
|
||||
var testData = new Uint8Array([0x41, 0x42, 0x43, 0x44]);
|
||||
var encryptedData = await crypto.subtle.encrypt({name: "RSA-OAEP"}, publicKey, testData);
|
||||
var testDecryptedData = await crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, encryptedData);
|
||||
|
||||
return areBuffersEqual(testData, testDecryptedData);
|
||||
}
|
||||
|
||||
function areBuffersEqual(buf1, buf2)
|
||||
{
|
||||
if (buf1.byteLength != buf2.byteLength) return false;
|
||||
var dv1 = new Int8Array(buf1);
|
||||
var dv2 = new Int8Array(buf2);
|
||||
for (var i = 0 ; i != buf1.byteLength ; i++)
|
||||
{
|
||||
if (dv1[i] != dv2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function concatBuffers(arrays)
|
||||
{
|
||||
// Get the total length of all arrays.
|
||||
let length = 0;
|
||||
arrays.forEach(item => {
|
||||
length += item.length;
|
||||
});
|
||||
|
||||
// Create a new array with total length and merge all source arrays.
|
||||
let mergedArray = new Uint8Array(length);
|
||||
let offset = 0;
|
||||
arrays.forEach(item => {
|
||||
mergedArray.set(new Uint8Array(item), offset);
|
||||
offset += item.length;
|
||||
});
|
||||
|
||||
return mergedArray;
|
||||
}
|
||||
|
||||
// CryptoJS format to byte array
|
||||
function wordToByteArray(wordArray)
|
||||
{
|
||||
var byteArray = [], word, i, j;
|
||||
for (i = 0; i < wordArray.length; ++i) {
|
||||
word = wordArray[i];
|
||||
for (j = 3; j >= 0; --j) {
|
||||
byteArray.push((word >> 8 * j) & 0xFF);
|
||||
}
|
||||
}
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
// byte array to CryptoJS format
|
||||
function arrayToWordArray(u8Array)
|
||||
{
|
||||
var words = [], i = 0, len = u8Array.length;
|
||||
|
||||
while (i < len) {
|
||||
words.push(
|
||||
(u8Array[i++] << 24) |
|
||||
(u8Array[i++] << 16) |
|
||||
(u8Array[i++] << 8) |
|
||||
(u8Array[i++])
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
sigBytes: len,
|
||||
words: words
|
||||
};
|
||||
}
|
||||
|
||||
const toHexString = bytes => bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
|
||||
|
||||
const intToBuffer = num =>
|
||||
{
|
||||
let b = new ArrayBuffer(4);
|
||||
new DataView(b).setUint32(0, num);
|
||||
return Array.from(new Uint8Array(b));
|
||||
}
|
||||
|
||||
function PEM2Binary(pem)
|
||||
{
|
||||
var encoded = '';
|
||||
var lines = pem.split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (lines[i].indexOf('-----') < 0) {
|
||||
encoded += lines[i];
|
||||
}
|
||||
}
|
||||
var byteStr = atob(encoded);
|
||||
var bytes = new Uint8Array(byteStr.length);
|
||||
for (var i = 0; i < byteStr.length; i++) {
|
||||
bytes[i] = byteStr.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
}());
|
|
@ -0,0 +1,26 @@
|
|||
injectScripts();
|
||||
|
||||
async function injectScripts()
|
||||
{
|
||||
await injectScript('lib/pbf.3.0.5.min.js');
|
||||
await injectScript('lib/cryptojs-aes_0.2.0.min.js');
|
||||
await injectScript('protobuf-generated/license_protocol.proto.js');
|
||||
|
||||
|
||||
await injectScript('content_key_decryption.js');
|
||||
await injectScript('eme_interception.js');
|
||||
}
|
||||
|
||||
function injectScript(scriptName)
|
||||
{
|
||||
return new Promise(function(resolve, reject)
|
||||
{
|
||||
var s = document.createElement('script');
|
||||
s.src = chrome.extension.getURL(scriptName);
|
||||
s.onload = function() {
|
||||
this.parentNode.removeChild(this);
|
||||
resolve(true);
|
||||
};
|
||||
(document.head||document.documentElement).appendChild(s);
|
||||
});
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,418 @@
|
|||
/**
|
||||
* Hooks EME calls and forwards them for analysis and decryption.
|
||||
*
|
||||
* Most of the code here was borrowed from https://github.com/google/eme_logger/blob/master/eme_listeners.js
|
||||
*/
|
||||
|
||||
var lastReceivedLicenseRequest = null;
|
||||
var lastReceivedLicenseResponse = null;
|
||||
|
||||
/** Set up the EME listeners. */
|
||||
function startEMEInterception()
|
||||
{
|
||||
var listener = new EmeInterception();
|
||||
listener.setUpListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called whenever an EME method is getting called or an EME event fires
|
||||
*/
|
||||
EmeInterception.onOperation = function(operationType, args)
|
||||
{
|
||||
if (operationType == "GenerateRequestCall")
|
||||
{
|
||||
// got initData
|
||||
// console.log(args);
|
||||
}
|
||||
else if (operationType == "MessageEvent")
|
||||
{
|
||||
var licenseRequest = args.message;
|
||||
lastReceivedLicenseRequest = licenseRequest;
|
||||
}
|
||||
else if (operationType == "UpdateCall")
|
||||
{
|
||||
var licenseResponse = args[0];
|
||||
lastReceivedLicenseResponse = licenseResponse;
|
||||
|
||||
// OK, let's try to decrypt it, assuming the response correlates to the request
|
||||
WidevineCrypto.decryptContentKey(lastReceivedLicenseRequest, lastReceivedLicenseResponse);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Manager for EME event and method listeners.
|
||||
* @constructor
|
||||
*/
|
||||
function EmeInterception()
|
||||
{
|
||||
this.unprefixedEmeEnabled = Navigator.prototype.requestMediaKeySystemAccess ? true : false;
|
||||
this.prefixedEmeEnabled = HTMLMediaElement.prototype.webkitGenerateKeyRequest ? true : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The number of types of HTML Media Elements to track.
|
||||
* @const {number}
|
||||
*/
|
||||
EmeInterception.NUM_MEDIA_ELEMENT_TYPES = 3;
|
||||
|
||||
|
||||
/**
|
||||
* Sets up EME listeners for whichever type of EME is enabled.
|
||||
*/
|
||||
EmeInterception.prototype.setUpListeners = function()
|
||||
{
|
||||
if (!this.unprefixedEmeEnabled && !this.prefixedEmeEnabled) {
|
||||
// EME is not enabled, just ignore
|
||||
return;
|
||||
}
|
||||
if (this.unprefixedEmeEnabled) {
|
||||
this.addListenersToNavigator_();
|
||||
}
|
||||
if (this.prefixedEmeEnabled) {
|
||||
// Prefixed EME is enabled
|
||||
}
|
||||
this.addListenersToAllEmeElements_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds listeners to the EME methods on the Navigator object.
|
||||
* @private
|
||||
*/
|
||||
EmeInterception.prototype.addListenersToNavigator_ = function()
|
||||
{
|
||||
if (navigator.listenersAdded_)
|
||||
return;
|
||||
|
||||
var originalRequestMediaKeySystemAccessFn = EmeInterception.extendEmeMethod(
|
||||
navigator,
|
||||
navigator.requestMediaKeySystemAccess,
|
||||
"RequestMediaKeySystemAccessCall");
|
||||
|
||||
navigator.requestMediaKeySystemAccess = function()
|
||||
{
|
||||
var options = arguments[1];
|
||||
|
||||
// slice "It is recommended that a robustness level be specified" warning
|
||||
var modifiedArguments = arguments;
|
||||
var modifiedOptions = EmeInterception.addRobustnessLevelIfNeeded(options);
|
||||
modifiedArguments[1] = modifiedOptions;
|
||||
|
||||
var result = originalRequestMediaKeySystemAccessFn.apply(null, modifiedArguments);
|
||||
// Attach listeners to returned MediaKeySystemAccess object
|
||||
return result.then(function(mediaKeySystemAccess)
|
||||
{
|
||||
this.addListenersToMediaKeySystemAccess_(mediaKeySystemAccess);
|
||||
return Promise.resolve(mediaKeySystemAccess);
|
||||
}.bind(this));
|
||||
|
||||
}.bind(this);
|
||||
|
||||
navigator.listenersAdded_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds listeners to the EME methods on a MediaKeySystemAccess object.
|
||||
* @param {MediaKeySystemAccess} mediaKeySystemAccess A MediaKeySystemAccess
|
||||
* object to add listeners to.
|
||||
* @private
|
||||
*/
|
||||
EmeInterception.prototype.addListenersToMediaKeySystemAccess_ = function(mediaKeySystemAccess)
|
||||
{
|
||||
if (mediaKeySystemAccess.listenersAdded_) {
|
||||
return;
|
||||
}
|
||||
mediaKeySystemAccess.originalGetConfiguration = mediaKeySystemAccess.getConfiguration;
|
||||
mediaKeySystemAccess.getConfiguration = EmeInterception.extendEmeMethod(
|
||||
mediaKeySystemAccess,
|
||||
mediaKeySystemAccess.getConfiguration,
|
||||
"GetConfigurationCall");
|
||||
|
||||
var originalCreateMediaKeysFn = EmeInterception.extendEmeMethod(
|
||||
mediaKeySystemAccess,
|
||||
mediaKeySystemAccess.createMediaKeys,
|
||||
"CreateMediaKeysCall");
|
||||
|
||||
mediaKeySystemAccess.createMediaKeys = function()
|
||||
{
|
||||
var result = originalCreateMediaKeysFn.apply(null, arguments);
|
||||
// Attach listeners to returned MediaKeys object
|
||||
return result.then(function(mediaKeys) {
|
||||
mediaKeys.keySystem_ = mediaKeySystemAccess.keySystem;
|
||||
this.addListenersToMediaKeys_(mediaKeys);
|
||||
return Promise.resolve(mediaKeys);
|
||||
}.bind(this));
|
||||
|
||||
}.bind(this);
|
||||
|
||||
mediaKeySystemAccess.listenersAdded_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds listeners to the EME methods on a MediaKeys object.
|
||||
* @param {MediaKeys} mediaKeys A MediaKeys object to add listeners to.
|
||||
* @private
|
||||
*/
|
||||
EmeInterception.prototype.addListenersToMediaKeys_ = function(mediaKeys)
|
||||
{
|
||||
if (mediaKeys.listenersAdded_) {
|
||||
return;
|
||||
}
|
||||
var originalCreateSessionFn = EmeInterception.extendEmeMethod(mediaKeys, mediaKeys.createSession, "CreateSessionCall");
|
||||
mediaKeys.createSession = function()
|
||||
{
|
||||
var result = originalCreateSessionFn.apply(null, arguments);
|
||||
result.keySystem_ = mediaKeys.keySystem_;
|
||||
// Attach listeners to returned MediaKeySession object
|
||||
this.addListenersToMediaKeySession_(result);
|
||||
return result;
|
||||
}.bind(this);
|
||||
|
||||
mediaKeys.setServerCertificate = EmeInterception.extendEmeMethod(mediaKeys, mediaKeys.setServerCertificate, "SetServerCertificateCall");
|
||||
mediaKeys.listenersAdded_ = true;
|
||||
};
|
||||
|
||||
|
||||
/** Adds listeners to the EME methods and events on a MediaKeySession object.
|
||||
* @param {MediaKeySession} session A MediaKeySession object to add
|
||||
* listeners to.
|
||||
* @private
|
||||
*/
|
||||
EmeInterception.prototype.addListenersToMediaKeySession_ = function(session)
|
||||
{
|
||||
if (session.listenersAdded_) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.generateRequest = EmeInterception.extendEmeMethod(session,session.generateRequest, "GenerateRequestCall");
|
||||
session.load = EmeInterception.extendEmeMethod(session, session.load, "LoadCall");
|
||||
session.update = EmeInterception.extendEmeMethod(session,session.update, "UpdateCall");
|
||||
session.close = EmeInterception.extendEmeMethod(session, session.close, "CloseCall");
|
||||
session.remove = EmeInterception.extendEmeMethod(session, session.remove, "RemoveCall");
|
||||
|
||||
session.addEventListener('message', function(e)
|
||||
{
|
||||
e.keySystem = session.keySystem_;
|
||||
EmeInterception.interceptEvent("MessageEvent", e);
|
||||
});
|
||||
|
||||
session.addEventListener('keystatuseschange', EmeInterception.interceptEvent.bind(null, "KeyStatusesChangeEvent"));
|
||||
|
||||
session.listenersAdded_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds listeners to all currently created media elements (audio, video) and sets up a
|
||||
* mutation-summary observer to add listeners to any newly created media
|
||||
* elements.
|
||||
* @private
|
||||
*/
|
||||
EmeInterception.prototype.addListenersToAllEmeElements_ = function()
|
||||
{
|
||||
this.addEmeInterceptionToInitialMediaElements_();
|
||||
|
||||
// TODO: Use MutationObserver directry
|
||||
// var observer = new MutationSummary({
|
||||
// callback: function(summaries) {
|
||||
// applyListeners(summaries);
|
||||
// },
|
||||
// queries: [{element: 'video'}, {element: 'audio'}, {element: 'media'}]
|
||||
// });
|
||||
|
||||
// var applyListeners = function(summaries) {
|
||||
// for (var i = 0; i < EmeInterception.NUM_MEDIA_ELEMENT_TYPES; i++) {
|
||||
// var elements = summaries[i];
|
||||
// elements.added.forEach(function(element) {
|
||||
// this.addListenersToEmeElement_(element, true);
|
||||
// }.bind(this));
|
||||
// }
|
||||
// }.bind(this);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds listeners to the EME elements currently in the document.
|
||||
* @private
|
||||
*/
|
||||
EmeInterception.prototype.addEmeInterceptionToInitialMediaElements_ = function()
|
||||
{
|
||||
var audioElements = document.getElementsByTagName('audio');
|
||||
for (var i = 0; i < audioElements.length; ++i) {
|
||||
this.addListenersToEmeElement_(audioElements[i], false);
|
||||
}
|
||||
var videoElements = document.getElementsByTagName('video');
|
||||
for (var i = 0; i < videoElements.length; ++i) {
|
||||
this.addListenersToEmeElement_(videoElements[i], false);
|
||||
}
|
||||
var mediaElements = document.getElementsByTagName('media');
|
||||
for (var i = 0; i < mediaElements.length; ++i) {
|
||||
this.addListenersToEmeElement_(mediaElements[i], false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds method and event listeners to media element.
|
||||
* @param {HTMLMediaElement} element A HTMLMedia element to add listeners to.
|
||||
* @private
|
||||
*/
|
||||
EmeInterception.prototype.addListenersToEmeElement_ = function(element)
|
||||
{
|
||||
this.addEmeEventListeners_(element);
|
||||
this.addEmeMethodListeners_(element);
|
||||
console.info('EME listeners successfully added to:', element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds event listeners to a media element.
|
||||
* @param {HTMLMediaElement} element A HTMLMedia element to add listeners to.
|
||||
* @private
|
||||
*/
|
||||
EmeInterception.prototype.addEmeEventListeners_ = function(element)
|
||||
{
|
||||
if (element.eventListenersAdded_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.prefixedEmeEnabled)
|
||||
{
|
||||
element.addEventListener('webkitneedkey', EmeInterception.interceptEvent.bind(null, "NeedKeyEvent"));
|
||||
element.addEventListener('webkitkeymessage', EmeInterception.interceptEvent.bind(null, "KeyMessageEvent"));
|
||||
element.addEventListener('webkitkeyadded', EmeInterception.interceptEvent.bind(null, "KeyAddedEvent"));
|
||||
element.addEventListener('webkitkeyerror', EmeInterception.interceptEvent.bind(null, "KeyErrorEvent"));
|
||||
}
|
||||
|
||||
element.addEventListener('encrypted', EmeInterception.interceptEvent.bind(null, "EncryptedEvent"));
|
||||
element.addEventListener('play', EmeInterception.interceptEvent.bind(null, "PlayEvent"));
|
||||
|
||||
element.addEventListener('error', function(e) {
|
||||
console.error('Error Event');
|
||||
EmeInterception.interceptEvent("ErrorEvent", e);
|
||||
});
|
||||
|
||||
element.eventListenersAdded_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds method listeners to a media element.
|
||||
* @param {HTMLMediaElement} element A HTMLMedia element to add listeners to.
|
||||
* @private
|
||||
*/
|
||||
EmeInterception.prototype.addEmeMethodListeners_ = function(element)
|
||||
{
|
||||
if (element.methodListenersAdded_) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.play = EmeInterception.extendEmeMethod(element, element.play, "PlayCall");
|
||||
|
||||
if (this.prefixedEmeEnabled) {
|
||||
element.canPlayType = EmeInterception.extendEmeMethod(element, element.canPlayType, "CanPlayTypeCall");
|
||||
|
||||
element.webkitGenerateKeyRequest = EmeInterception.extendEmeMethod(element, element.webkitGenerateKeyRequest, "GenerateKeyRequestCall");
|
||||
element.webkitAddKey = EmeInterception.extendEmeMethod(element, element.webkitAddKey, "AddKeyCall");
|
||||
element.webkitCancelKeyRequest = EmeInterception.extendEmeMethod(element, element.webkitCancelKeyRequest, "CancelKeyRequestCall");
|
||||
|
||||
}
|
||||
|
||||
if (this.unprefixedEmeEnabled) {
|
||||
element.setMediaKeys = EmeInterception.extendEmeMethod(element, element.setMediaKeys, "SetMediaKeysCall");
|
||||
}
|
||||
|
||||
element.methodListenersAdded_ = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a wrapper function that logs calls to the given method.
|
||||
* @param {!Object} element An element or object whose function
|
||||
* call will be logged.
|
||||
* @param {!Function} originalFn The function to log.
|
||||
* @param {!Function} type The constructor for a logger class that will
|
||||
* be instantiated to log the originalFn call.
|
||||
* @return {!Function} The new version, with logging, of orginalFn.
|
||||
*/
|
||||
EmeInterception.extendEmeMethod = function(element, originalFn, type)
|
||||
{
|
||||
return function()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = originalFn.apply(element, arguments);
|
||||
var args = [].slice.call(arguments);
|
||||
EmeInterception.interceptCall(type, args, result, element);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Intercepts a method call to the console and a separate frame.
|
||||
* @param {!Function} constructor The constructor for a logger class that will
|
||||
* be instantiated to log this call.
|
||||
* @param {Array} args The arguments this call was made with.
|
||||
* @param {Object} result The result of this method call.
|
||||
* @param {!Object} target The element this method was called on.
|
||||
* @return {!eme.EmeMethodCall} The data that has been logged.
|
||||
*/
|
||||
EmeInterception.interceptCall = function(type, args, result, target)
|
||||
{
|
||||
EmeInterception.onOperation(type, args);
|
||||
return args;
|
||||
};
|
||||
|
||||
/**
|
||||
* Intercepts an event to the console and a separate frame.
|
||||
* @param {!Function} constructor The constructor for a logger class that will
|
||||
* be instantiated to log this event.
|
||||
* @param {!Event} event An EME event.
|
||||
* @return {!eme.EmeEvent} The data that has been logged.
|
||||
*/
|
||||
EmeInterception.interceptEvent = function(type, event)
|
||||
{
|
||||
EmeInterception.onOperation(type, event);
|
||||
return event;
|
||||
};
|
||||
|
||||
EmeInterception.addRobustnessLevelIfNeeded = function(options)
|
||||
{
|
||||
for (var i = 0; i < options.length; i++)
|
||||
{
|
||||
var option = options[i];
|
||||
var videoCapabilities = option["videoCapabilities"];
|
||||
var audioCapabilties = option["audioCapabilities"];
|
||||
if (videoCapabilities != null)
|
||||
{
|
||||
for (var j = 0; j < videoCapabilities.length; j++)
|
||||
if (videoCapabilities[j]["robustness"] == undefined) videoCapabilities[j]["robustness"] = "SW_SECURE_CRYPTO";
|
||||
}
|
||||
|
||||
if (audioCapabilties != null)
|
||||
{
|
||||
for (var j = 0; j < audioCapabilties.length; j++)
|
||||
if (audioCapabilties[j]["robustness"] == undefined) audioCapabilties[j]["robustness"] = "SW_SECURE_CRYPTO";
|
||||
}
|
||||
|
||||
option["videoCapabilities"] = videoCapabilities;
|
||||
option["audioCapabilities"] = audioCapabilties;
|
||||
options[i] = option;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
startEMEInterception();
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,549 @@
|
|||
// ----------------------------------------------------------------------------
|
||||
// license_protocol.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// Definitions of the protocol buffer messages used in the Widevine license
|
||||
// exchange protocol.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package video_widevine_server.sdk;
|
||||
|
||||
// need this if we are using libprotobuf-cpp-2.3.0-lite
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
enum LicenseType {
|
||||
STREAMING = 1;
|
||||
OFFLINE = 2;
|
||||
}
|
||||
|
||||
// LicenseIdentification is propagated from LicenseRequest to License,
|
||||
// incrementing version with each iteration.
|
||||
message LicenseIdentification {
|
||||
optional bytes request_id = 1;
|
||||
optional bytes session_id = 2;
|
||||
optional bytes purchase_id = 3;
|
||||
optional LicenseType type = 4;
|
||||
optional int32 version = 5;
|
||||
optional bytes provider_session_token = 6;
|
||||
}
|
||||
|
||||
message License {
|
||||
message Policy {
|
||||
// Indicates that playback of the content is allowed.
|
||||
optional bool can_play = 1 [default = false];
|
||||
|
||||
// Indicates that the license may be persisted to non-volatile
|
||||
// storage for offline use.
|
||||
optional bool can_persist = 2 [default = false];
|
||||
|
||||
// Indicates that renewal of this license is allowed.
|
||||
optional bool can_renew = 3 [default = false];
|
||||
|
||||
// For the |*duration*| fields, playback must halt when
|
||||
// license_start_time (seconds since the epoch (UTC)) +
|
||||
// license_duration_seconds is exceeded. A value of 0
|
||||
// indicates that there is no limit to the duration.
|
||||
|
||||
// Indicates the rental window.
|
||||
optional int64 rental_duration_seconds = 4 [default = 0];
|
||||
|
||||
// Indicates the viewing window, once playback has begun.
|
||||
optional int64 playback_duration_seconds = 5 [default = 0];
|
||||
|
||||
// Indicates the time window for this specific license.
|
||||
optional int64 license_duration_seconds = 6 [default = 0];
|
||||
|
||||
// The |renewal*| fields only apply if |can_renew| is true.
|
||||
|
||||
// The window of time, in which playback is allowed to continue while
|
||||
// renewal is attempted, yet unsuccessful due to backend problems with
|
||||
// the license server.
|
||||
optional int64 renewal_recovery_duration_seconds = 7 [default = 0];
|
||||
|
||||
// All renewal requests for this license shall be directed to the
|
||||
// specified URL.
|
||||
optional string renewal_server_url = 8;
|
||||
|
||||
// How many seconds after license_start_time, before renewal is first
|
||||
// attempted.
|
||||
optional int64 renewal_delay_seconds = 9 [default = 0];
|
||||
|
||||
// Specifies the delay in seconds between subsequent license
|
||||
// renewal requests, in case of failure.
|
||||
optional int64 renewal_retry_interval_seconds = 10 [default = 0];
|
||||
|
||||
// Indicates that the license shall be sent for renewal when usage is
|
||||
// started.
|
||||
optional bool renew_with_usage = 11 [default = false];
|
||||
|
||||
// Indicates to client that license renewal and release requests ought to
|
||||
// include ClientIdentification (client_id).
|
||||
optional bool renew_with_client_id = 12 [default = false];
|
||||
}
|
||||
|
||||
message KeyContainer {
|
||||
enum KeyType {
|
||||
// Exactly one key of this type must appear.
|
||||
SIGNING = 1;
|
||||
CONTENT = 2;
|
||||
KEY_CONTROL = 3;
|
||||
OPERATOR_SESSION = 4;
|
||||
}
|
||||
|
||||
// The SecurityLevel enumeration allows the server to communicate the level
|
||||
// of robustness required by the client, in order to use the key.
|
||||
enum SecurityLevel {
|
||||
// Software-based whitebox crypto is required.
|
||||
SW_SECURE_CRYPTO = 1;
|
||||
|
||||
// Software crypto and an obfuscated decoder is required.
|
||||
SW_SECURE_DECODE = 2;
|
||||
|
||||
// The key material and crypto operations must be performed within a
|
||||
// hardware backed trusted execution environment.
|
||||
HW_SECURE_CRYPTO = 3;
|
||||
|
||||
// The crypto and decoding of content must be performed within a hardware
|
||||
// backed trusted execution environment.
|
||||
HW_SECURE_DECODE = 4;
|
||||
|
||||
// The crypto, decoding and all handling of the media (compressed and
|
||||
// uncompressed) must be handled within a hardware backed trusted
|
||||
// execution environment.
|
||||
HW_SECURE_ALL = 5;
|
||||
}
|
||||
|
||||
message KeyControl {
|
||||
// If present, the key control must be communicated to the secure
|
||||
// environment prior to any usage. This message is automatically generated
|
||||
// by the Widevine License Server SDK.
|
||||
optional bytes key_control_block = 1;
|
||||
optional bytes iv = 2;
|
||||
}
|
||||
|
||||
message OutputProtection {
|
||||
// Indicates whether HDCP is required on digital outputs, and which
|
||||
// version should be used.
|
||||
enum HDCP {
|
||||
HDCP_NONE = 0;
|
||||
HDCP_V1 = 1;
|
||||
HDCP_V2 = 2;
|
||||
HDCP_V2_1 = 3;
|
||||
HDCP_V2_2 = 4;
|
||||
HDCP_NO_DIGITAL_OUTPUT = 0xff;
|
||||
}
|
||||
optional HDCP hdcp = 1 [default = HDCP_NONE];
|
||||
|
||||
// Indicate the CGMS setting to be inserted on analog output.
|
||||
enum CGMS {
|
||||
CGMS_NONE = 42;
|
||||
COPY_FREE = 0;
|
||||
COPY_ONCE = 2;
|
||||
COPY_NEVER = 3;
|
||||
}
|
||||
optional CGMS cgms_flags = 2 [default = CGMS_NONE];
|
||||
}
|
||||
|
||||
message VideoResolutionConstraint {
|
||||
// Minimum and maximum video resolutions in the range (height x width).
|
||||
optional uint32 min_resolution_pixels = 1;
|
||||
optional uint32 max_resolution_pixels = 2;
|
||||
// Optional output protection requirements for this range. If not
|
||||
// specified, the OutputProtection in the KeyContainer applies.
|
||||
optional OutputProtection required_protection = 3;
|
||||
}
|
||||
|
||||
message OperatorSessionKeyPermissions {
|
||||
// Permissions/key usage flags for operator service keys
|
||||
// (type = OPERATOR_SESSION).
|
||||
optional bool allow_encrypt = 1 [default = false];
|
||||
optional bool allow_decrypt = 2 [default = false];
|
||||
optional bool allow_sign = 3 [default = false];
|
||||
optional bool allow_signature_verify = 4 [default = false];
|
||||
}
|
||||
|
||||
optional bytes id = 1;
|
||||
optional bytes iv = 2;
|
||||
optional bytes key = 3;
|
||||
optional KeyType type = 4;
|
||||
optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO];
|
||||
optional OutputProtection required_protection = 6;
|
||||
// NOTE: Use of requested_protection is not recommended as it is only
|
||||
// supported on a small number of platforms.
|
||||
optional OutputProtection requested_protection = 7;
|
||||
optional KeyControl key_control = 8;
|
||||
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
|
||||
// Optional video resolution constraints. If the video resolution of the
|
||||
// content being decrypted/decoded falls within one of the specified ranges,
|
||||
// the optional required_protections may be applied. Otherwise an error will
|
||||
// be reported.
|
||||
// NOTE: Use of this feature is not recommended, as it is only supported on
|
||||
// a small number of platforms.
|
||||
repeated VideoResolutionConstraint video_resolution_constraints = 10;
|
||||
// Optional flag to indicate the key must only be used if the client
|
||||
// supports anti rollback of the user table. Content provider can query the
|
||||
// client capabilities to determine if the client support this feature.
|
||||
optional bool anti_rollback_usage_table = 11 [default = false];
|
||||
}
|
||||
|
||||
optional LicenseIdentification id = 1;
|
||||
optional Policy policy = 2;
|
||||
repeated KeyContainer key = 3;
|
||||
optional int64 license_start_time = 4;
|
||||
optional bool remote_attestation_verified = 5 [default = false];
|
||||
// Client token generated by the content provider. Optional.
|
||||
optional bytes provider_client_token = 6;
|
||||
}
|
||||
|
||||
enum ProtocolVersion {
|
||||
VERSION_2_0 = 20;
|
||||
VERSION_2_1 = 21;
|
||||
}
|
||||
|
||||
message LicenseRequest {
|
||||
message ContentIdentification {
|
||||
message CENC {
|
||||
repeated bytes pssh = 1;
|
||||
optional LicenseType license_type = 2;
|
||||
optional bytes request_id = 3; // Opaque, client-specified.
|
||||
}
|
||||
|
||||
message WebM {
|
||||
optional bytes header = 1;
|
||||
optional LicenseType license_type = 2;
|
||||
optional bytes request_id = 3; // Opaque, client-specified.
|
||||
}
|
||||
|
||||
message ExistingLicense {
|
||||
optional LicenseIdentification license_id = 1;
|
||||
optional int64 seconds_since_started = 2;
|
||||
optional int64 seconds_since_last_played = 3;
|
||||
optional bytes session_usage_table_entry = 4;
|
||||
}
|
||||
|
||||
// Exactly one of these must be present.
|
||||
optional CENC cenc_id = 1;
|
||||
optional WebM webm_id = 2;
|
||||
optional ExistingLicense license = 3;
|
||||
}
|
||||
|
||||
enum RequestType {
|
||||
NEW = 1;
|
||||
RENEWAL = 2;
|
||||
RELEASE = 3;
|
||||
}
|
||||
|
||||
// The client_id provides information authenticating the calling device. It
|
||||
// contains the Widevine keybox token that was installed on the device at the
|
||||
// factory. This field or encrypted_client_id below is required for a valid
|
||||
// license request, but both should never be present in the same request.
|
||||
optional ClientIdentification client_id = 1;
|
||||
optional ContentIdentification content_id = 2;
|
||||
optional RequestType type = 3;
|
||||
optional int64 request_time = 4;
|
||||
// Old-style decimal-encoded string key control nonce.
|
||||
optional bytes key_control_nonce_deprecated = 5;
|
||||
optional ProtocolVersion protocol_version = 6 [default = VERSION_2_0];
|
||||
// New-style uint32 key control nonce, please use instead of
|
||||
// key_control_nonce_deprecated.
|
||||
optional uint32 key_control_nonce = 7;
|
||||
// Encrypted ClientIdentification message, used for privacy purposes.
|
||||
optional EncryptedClientIdentification encrypted_client_id = 8;
|
||||
}
|
||||
|
||||
message LicenseError {
|
||||
enum Error {
|
||||
// The device credentials are invalid. The device must re-provision.
|
||||
INVALID_DEVICE_CERTIFICATE = 1;
|
||||
// The device credentials have been revoked. Re-provisioning is not
|
||||
// possible.
|
||||
REVOKED_DEVICE_CERTIFICATE = 2;
|
||||
// The service is currently unavailable due to the backend being down
|
||||
// or similar circumstances.
|
||||
SERVICE_UNAVAILABLE = 3;
|
||||
}
|
||||
optional Error error_code = 1;
|
||||
}
|
||||
|
||||
message RemoteAttestation {
|
||||
// Encrypted ClientIdentification message containing the device remote
|
||||
// attestation certificate. Required.
|
||||
optional EncryptedClientIdentification certificate = 1;
|
||||
// Bytes of salt which were added to the remote attestation challenge prior to
|
||||
// signing it. Required.
|
||||
optional bytes salt = 2;
|
||||
// Signed remote attestation challenge + salt. Required.
|
||||
optional bytes signature = 3;
|
||||
}
|
||||
|
||||
message SignedMessage {
|
||||
enum MessageType {
|
||||
LICENSE_REQUEST = 1;
|
||||
LICENSE = 2;
|
||||
ERROR_RESPONSE = 3;
|
||||
SERVICE_CERTIFICATE_REQUEST = 4;
|
||||
SERVICE_CERTIFICATE = 5;
|
||||
}
|
||||
|
||||
optional MessageType type = 1;
|
||||
optional bytes msg = 2;
|
||||
optional bytes signature = 3;
|
||||
optional bytes session_key = 4;
|
||||
// Remote attestation data which will be present in the initial license
|
||||
// request for ChromeOS client devices operating in verified mode. Remote
|
||||
// attestation challenge data is |msg| field above. Optional.
|
||||
optional RemoteAttestation remote_attestation = 5;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// certificate_provisioning.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// Public protocol buffer definitions for Widevine Device Certificate
|
||||
// Provisioning protocol.
|
||||
|
||||
// ProvisioningOptions specifies the type of certificate to specify and
|
||||
// in the case of X509 certificates, the certificate authority to use.
|
||||
message ProvisioningOptions {
|
||||
enum CertificateType {
|
||||
WIDEVINE_DRM = 0; // Default. The original certificate type.
|
||||
X509 = 1; // X.509 certificate.
|
||||
}
|
||||
|
||||
optional CertificateType certificate_type = 1;
|
||||
|
||||
// It is recommended that the certificate_authority specify the X.509
|
||||
// Subject of the signing certificate.
|
||||
optional string certificate_authority = 2;
|
||||
}
|
||||
|
||||
// Provisioning request sent by client devices to provisioning service.
|
||||
message ProvisioningRequest {
|
||||
// Device root of trust and other client identification. Required.
|
||||
optional ClientIdentification client_id = 1;
|
||||
// Nonce value used to prevent replay attacks. Required.
|
||||
optional bytes nonce = 2;
|
||||
// Options for type of certificate to generate. Optional.
|
||||
optional ProvisioningOptions options = 3;
|
||||
// Stable identifier, unique for each device + application (or origin).
|
||||
// Required if doing per-origin provisioning.
|
||||
optional bytes stable_id = 4;
|
||||
}
|
||||
|
||||
// Provisioning response sent by the provisioning server to client devices.
|
||||
message ProvisioningResponse {
|
||||
// AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded.
|
||||
// Required.
|
||||
optional bytes device_rsa_key = 1;
|
||||
// Initialization vector used to encrypt device_rsa_key. Required.
|
||||
optional bytes device_rsa_key_iv = 2;
|
||||
// Serialized SignedDeviceCertificate. Required.
|
||||
optional bytes device_certificate = 3;
|
||||
// Nonce value matching nonce in ProvisioningRequest. Required.
|
||||
optional bytes nonce = 4;
|
||||
}
|
||||
|
||||
// Serialized ProvisioningRequest or ProvisioningResponse signed with
|
||||
// The message authentication key.
|
||||
message SignedProvisioningMessage {
|
||||
// Serialized ProvisioningRequest or ProvisioningResponse. Required.
|
||||
optional bytes message = 1;
|
||||
// HMAC-SHA256 signature of message. Required.
|
||||
optional bytes signature = 2;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// client_identification.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// ClientIdentification messages used by provisioning and license protocols.
|
||||
|
||||
// ClientIdentification message used to authenticate the client device.
|
||||
message ClientIdentification {
|
||||
enum TokenType {
|
||||
KEYBOX = 0;
|
||||
DEVICE_CERTIFICATE = 1;
|
||||
REMOTE_ATTESTATION_CERTIFICATE = 2;
|
||||
}
|
||||
|
||||
message NameValue {
|
||||
optional string name = 1;
|
||||
optional string value = 2;
|
||||
}
|
||||
|
||||
// Capabilities which not all clients may support. Used for the license
|
||||
// exchange protocol only.
|
||||
message ClientCapabilities {
|
||||
enum HdcpVersion {
|
||||
HDCP_NONE = 0;
|
||||
HDCP_V1 = 1;
|
||||
HDCP_V2 = 2;
|
||||
HDCP_V2_1 = 3;
|
||||
HDCP_V2_2 = 4;
|
||||
HDCP_NO_DIGITAL_OUTPUT = 0xff;
|
||||
}
|
||||
|
||||
optional bool client_token = 1 [default = false];
|
||||
optional bool session_token = 2 [default = false];
|
||||
optional bool video_resolution_constraints = 3 [default = false];
|
||||
optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE];
|
||||
optional uint32 oem_crypto_api_version = 5;
|
||||
optional bool anti_rollback_usage_table = 6 [default = false];
|
||||
}
|
||||
|
||||
// Type of factory-provisioned device root of trust. Optional.
|
||||
optional TokenType type = 1 [default = KEYBOX];
|
||||
// Factory-provisioned device root of trust. Required.
|
||||
optional bytes token = 2;
|
||||
// Optional client information name/value pairs.
|
||||
repeated NameValue client_info = 3;
|
||||
// Client token generated by the content provider. Optional.
|
||||
optional bytes provider_client_token = 4;
|
||||
// Number of licenses received by the client to which the token above belongs.
|
||||
// Only present if client_token is specified.
|
||||
optional uint32 license_counter = 5;
|
||||
// List of non-baseline client capabilities.
|
||||
optional ClientCapabilities client_capabilities = 6;
|
||||
}
|
||||
|
||||
// EncryptedClientIdentification message used to hold ClientIdentification
|
||||
// messages encrypted for privacy purposes.
|
||||
message EncryptedClientIdentification {
|
||||
// Service ID for which the ClientIdentifcation is encrypted (owner of service
|
||||
// certificate).
|
||||
optional string service_id = 1;
|
||||
// Serial number for the service certificate for which ClientIdentification is
|
||||
// encrypted.
|
||||
optional bytes service_certificate_serial_number = 2;
|
||||
// Serialized ClientIdentification message, encrypted with the privacy key using
|
||||
// AES-128-CBC with PKCS#5 padding.
|
||||
optional bytes encrypted_client_id = 3;
|
||||
// Initialization vector needed to decrypt encrypted_client_id.
|
||||
optional bytes encrypted_client_id_iv = 4;
|
||||
// AES-128 privacy key, encrytped with the service public public key using
|
||||
// RSA-OAEP.
|
||||
optional bytes encrypted_privacy_key = 5;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// device_certificate.proto
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Description:
|
||||
// Device certificate and certificate status list format definitions.
|
||||
|
||||
// Certificate definition for user devices, intermediate, service, and root
|
||||
// certificates.
|
||||
message DeviceCertificate {
|
||||
enum CertificateType {
|
||||
ROOT = 0;
|
||||
INTERMEDIATE = 1;
|
||||
USER_DEVICE = 2;
|
||||
SERVICE = 3;
|
||||
}
|
||||
|
||||
// Type of certificate. Required.
|
||||
optional CertificateType type = 1;
|
||||
// 128-bit globally unique serial number of certificate.
|
||||
// Value is 0 for root certificate. Required.
|
||||
optional bytes serial_number = 2;
|
||||
// POSIX time, in seconds, when the certificate was created. Required.
|
||||
optional uint32 creation_time_seconds = 3;
|
||||
// Device public key. PKCS#1 ASN.1 DER-encoded. Required.
|
||||
optional bytes public_key = 4;
|
||||
// Widevine system ID for the device. Required for intermediate and
|
||||
// user device certificates.
|
||||
optional uint32 system_id = 5;
|
||||
// Deprecated field, which used to indicate whether the device was a test
|
||||
// (non-production) device. The test_device field in ProvisionedDeviceInfo
|
||||
// below should be observed instead.
|
||||
optional bool test_device_deprecated = 6 [deprecated = true];
|
||||
// Service identifier (web origin) for the service which owns the certificate.
|
||||
// Required for service certificates.
|
||||
optional string service_id = 7;
|
||||
}
|
||||
|
||||
// DeviceCertificate signed with intermediate or root certificate private key.
|
||||
message SignedDeviceCertificate {
|
||||
// Serialized DeviceCertificate. Required.
|
||||
optional bytes device_certificate = 1;
|
||||
// Signature of device_certificate. Signed with root or intermediate
|
||||
// certificate private key using RSASSA-PSS. Required.
|
||||
optional bytes signature = 2;
|
||||
// Intermediate signing certificate. Present only for user device
|
||||
// certificates. All others signed with root certificate private key.
|
||||
optional SignedDeviceCertificate signer = 3;
|
||||
}
|
||||
|
||||
// Contains device model information for a provisioned device.
|
||||
message ProvisionedDeviceInfo {
|
||||
enum WvSecurityLevel {
|
||||
// Defined in "WV Modular DRM Security Integration Guide for
|
||||
// Common Encryption (CENC)"
|
||||
LEVEL_UNSPECIFIED = 0;
|
||||
LEVEL_1 = 1;
|
||||
LEVEL_2 = 2;
|
||||
LEVEL_3 = 3;
|
||||
}
|
||||
|
||||
// Widevine system ID for the device. Mandatory.
|
||||
optional uint32 system_id = 1;
|
||||
// Name of system-on-a-chip. Optional.
|
||||
optional string soc = 2;
|
||||
// Name of manufacturer. Optional.
|
||||
optional string manufacturer = 3;
|
||||
// Manufacturer's model name. Matches "brand" in device metadata. Optional.
|
||||
optional string model = 4;
|
||||
// Type of device (Phone, Tablet, TV, etc).
|
||||
optional string device_type = 5;
|
||||
// Device model year. Optional.
|
||||
optional uint32 model_year = 6;
|
||||
// Widevine-defined security level. Optional.
|
||||
optional WvSecurityLevel security_level = 7 [default = LEVEL_UNSPECIFIED];
|
||||
// True if the certificate corresponds to a test (non production) device.
|
||||
// Optional.
|
||||
optional bool test_device = 8 [default = false];
|
||||
}
|
||||
|
||||
// Contains the status of the root or an intermediate DeviceCertificate.
|
||||
message DeviceCertificateStatus {
|
||||
enum CertificateStatus {
|
||||
VALID = 0;
|
||||
REVOKED = 1;
|
||||
};
|
||||
|
||||
// Serial number of the DeviceCertificate to which this message refers.
|
||||
// Required.
|
||||
optional bytes serial_number = 1;
|
||||
// Status of the certificate. Optional.
|
||||
optional CertificateStatus status = 2 [default = VALID];
|
||||
// Device model information about the device to which the certificate
|
||||
// corresponds. Required.
|
||||
optional ProvisionedDeviceInfo device_info = 4;
|
||||
}
|
||||
|
||||
// List of DeviceCertificateStatus. Used to propagate certificate revocation and
|
||||
// update list.
|
||||
message DeviceCertificateStatusList {
|
||||
// POSIX time, in seconds, when the list was created. Required.
|
||||
optional uint32 creation_time_seconds = 1;
|
||||
// DeviceCertificateStatus for each certifificate.
|
||||
repeated DeviceCertificateStatus certificate_status = 2;
|
||||
}
|
||||
|
||||
// Signed CertificateStatusList
|
||||
message SignedCertificateStatusList {
|
||||
// Serialized DeviceCertificateStatusList. Required.
|
||||
optional bytes certificate_status_list = 1;
|
||||
// Signature of certificate_status_list. Signed with root certificate private
|
||||
// key using RSASSA-PSS. Required.
|
||||
optional bytes signature = 2;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Widivine Decryptor",
|
||||
"short_name": "WidevineDecryptor",
|
||||
"description": "Decrypts and logs media keys from websites that use Widivine DRM",
|
||||
"version": "1.0.0",
|
||||
"permissions":
|
||||
[
|
||||
|
||||
],
|
||||
"icons":
|
||||
{
|
||||
|
||||
},
|
||||
"browser_action": {
|
||||
|
||||
},
|
||||
|
||||
"content_scripts":
|
||||
[
|
||||
{
|
||||
"matches": ["https://*/*"],
|
||||
"js": ["content_script.js"],
|
||||
"css": [],
|
||||
"run_at": "document_start"
|
||||
}
|
||||
],
|
||||
"web_accessible_resources": ["content_key_decryption.js", "eme_interception.js", "lib/*", "protobuf-generated/*"]
|
||||
}
|
|
@ -0,0 +1,890 @@
|
|||
'use strict'; // code generated by pbf v3.2.1
|
||||
|
||||
var LicenseType = self.LicenseType = {
|
||||
"STREAMING": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"OFFLINE": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
var ProtocolVersion = self.ProtocolVersion = {
|
||||
"VERSION_2_0": {
|
||||
"value": 20,
|
||||
"options": {}
|
||||
},
|
||||
"VERSION_2_1": {
|
||||
"value": 21,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// LicenseIdentification ========================================
|
||||
|
||||
var LicenseIdentification = self.LicenseIdentification = {};
|
||||
|
||||
LicenseIdentification.read = function (pbf, end) {
|
||||
return pbf.readFields(LicenseIdentification._readField, {request_id: null, session_id: null, purchase_id: null, type: 0, version: 0, provider_session_token: null}, end);
|
||||
};
|
||||
LicenseIdentification._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.request_id = pbf.readBytes();
|
||||
else if (tag === 2) obj.session_id = pbf.readBytes();
|
||||
else if (tag === 3) obj.purchase_id = pbf.readBytes();
|
||||
else if (tag === 4) obj.type = pbf.readVarint();
|
||||
else if (tag === 5) obj.version = pbf.readVarint(true);
|
||||
else if (tag === 6) obj.provider_session_token = pbf.readBytes();
|
||||
};
|
||||
LicenseIdentification.write = function (obj, pbf) {
|
||||
if (obj.request_id) pbf.writeBytesField(1, obj.request_id);
|
||||
if (obj.session_id) pbf.writeBytesField(2, obj.session_id);
|
||||
if (obj.purchase_id) pbf.writeBytesField(3, obj.purchase_id);
|
||||
if (obj.type) pbf.writeVarintField(4, obj.type);
|
||||
if (obj.version) pbf.writeVarintField(5, obj.version);
|
||||
if (obj.provider_session_token) pbf.writeBytesField(6, obj.provider_session_token);
|
||||
};
|
||||
|
||||
// License ========================================
|
||||
|
||||
var License = self.License = {};
|
||||
|
||||
License.read = function (pbf, end) {
|
||||
return pbf.readFields(License._readField, {id: null, policy: null, key: [], license_start_time: 0, remote_attestation_verified: false, provider_client_token: null}, end);
|
||||
};
|
||||
License._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.id = LicenseIdentification.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 2) obj.policy = License.Policy.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 3) obj.key.push(License.KeyContainer.read(pbf, pbf.readVarint() + pbf.pos));
|
||||
else if (tag === 4) obj.license_start_time = pbf.readVarint(true);
|
||||
else if (tag === 5) obj.remote_attestation_verified = pbf.readBoolean();
|
||||
else if (tag === 6) obj.provider_client_token = pbf.readBytes();
|
||||
};
|
||||
License.write = function (obj, pbf) {
|
||||
if (obj.id) pbf.writeMessage(1, LicenseIdentification.write, obj.id);
|
||||
if (obj.policy) pbf.writeMessage(2, License.Policy.write, obj.policy);
|
||||
if (obj.key) for (var i = 0; i < obj.key.length; i++) pbf.writeMessage(3, License.KeyContainer.write, obj.key[i]);
|
||||
if (obj.license_start_time) pbf.writeVarintField(4, obj.license_start_time);
|
||||
if (obj.remote_attestation_verified) pbf.writeBooleanField(5, obj.remote_attestation_verified);
|
||||
if (obj.provider_client_token) pbf.writeBytesField(6, obj.provider_client_token);
|
||||
};
|
||||
|
||||
// License.Policy ========================================
|
||||
|
||||
License.Policy = {};
|
||||
|
||||
License.Policy.read = function (pbf, end) {
|
||||
return pbf.readFields(License.Policy._readField, {can_play: false, can_persist: false, can_renew: false, rental_duration_seconds: 0, playback_duration_seconds: 0, license_duration_seconds: 0, renewal_recovery_duration_seconds: 0, renewal_server_url: "", renewal_delay_seconds: 0, renewal_retry_interval_seconds: 0, renew_with_usage: false, renew_with_client_id: false}, end);
|
||||
};
|
||||
License.Policy._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.can_play = pbf.readBoolean();
|
||||
else if (tag === 2) obj.can_persist = pbf.readBoolean();
|
||||
else if (tag === 3) obj.can_renew = pbf.readBoolean();
|
||||
else if (tag === 4) obj.rental_duration_seconds = pbf.readVarint(true);
|
||||
else if (tag === 5) obj.playback_duration_seconds = pbf.readVarint(true);
|
||||
else if (tag === 6) obj.license_duration_seconds = pbf.readVarint(true);
|
||||
else if (tag === 7) obj.renewal_recovery_duration_seconds = pbf.readVarint(true);
|
||||
else if (tag === 8) obj.renewal_server_url = pbf.readString();
|
||||
else if (tag === 9) obj.renewal_delay_seconds = pbf.readVarint(true);
|
||||
else if (tag === 10) obj.renewal_retry_interval_seconds = pbf.readVarint(true);
|
||||
else if (tag === 11) obj.renew_with_usage = pbf.readBoolean();
|
||||
else if (tag === 12) obj.renew_with_client_id = pbf.readBoolean();
|
||||
};
|
||||
License.Policy.write = function (obj, pbf) {
|
||||
if (obj.can_play) pbf.writeBooleanField(1, obj.can_play);
|
||||
if (obj.can_persist) pbf.writeBooleanField(2, obj.can_persist);
|
||||
if (obj.can_renew) pbf.writeBooleanField(3, obj.can_renew);
|
||||
if (obj.rental_duration_seconds) pbf.writeVarintField(4, obj.rental_duration_seconds);
|
||||
if (obj.playback_duration_seconds) pbf.writeVarintField(5, obj.playback_duration_seconds);
|
||||
if (obj.license_duration_seconds) pbf.writeVarintField(6, obj.license_duration_seconds);
|
||||
if (obj.renewal_recovery_duration_seconds) pbf.writeVarintField(7, obj.renewal_recovery_duration_seconds);
|
||||
if (obj.renewal_server_url) pbf.writeStringField(8, obj.renewal_server_url);
|
||||
if (obj.renewal_delay_seconds) pbf.writeVarintField(9, obj.renewal_delay_seconds);
|
||||
if (obj.renewal_retry_interval_seconds) pbf.writeVarintField(10, obj.renewal_retry_interval_seconds);
|
||||
if (obj.renew_with_usage) pbf.writeBooleanField(11, obj.renew_with_usage);
|
||||
if (obj.renew_with_client_id) pbf.writeBooleanField(12, obj.renew_with_client_id);
|
||||
};
|
||||
|
||||
// License.KeyContainer ========================================
|
||||
|
||||
License.KeyContainer = {};
|
||||
|
||||
License.KeyContainer.read = function (pbf, end) {
|
||||
return pbf.readFields(License.KeyContainer._readField, {id: null, iv: null, key: null, type: 0, level: {"value":1,"options":{}}, required_protection: null, requested_protection: null, key_control: null, operator_session_key_permissions: null, video_resolution_constraints: [], anti_rollback_usage_table: false}, end);
|
||||
};
|
||||
License.KeyContainer._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.id = pbf.readBytes();
|
||||
else if (tag === 2) obj.iv = pbf.readBytes();
|
||||
else if (tag === 3) obj.key = pbf.readBytes();
|
||||
else if (tag === 4) obj.type = pbf.readVarint();
|
||||
else if (tag === 5) obj.level = pbf.readVarint();
|
||||
else if (tag === 6) obj.required_protection = License.KeyContainer.OutputProtection.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 7) obj.requested_protection = License.KeyContainer.OutputProtection.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 8) obj.key_control = License.KeyContainer.KeyControl.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 9) obj.operator_session_key_permissions = License.KeyContainer.OperatorSessionKeyPermissions.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 10) obj.video_resolution_constraints.push(License.KeyContainer.VideoResolutionConstraint.read(pbf, pbf.readVarint() + pbf.pos));
|
||||
else if (tag === 11) obj.anti_rollback_usage_table = pbf.readBoolean();
|
||||
};
|
||||
License.KeyContainer.write = function (obj, pbf) {
|
||||
if (obj.id) pbf.writeBytesField(1, obj.id);
|
||||
if (obj.iv) pbf.writeBytesField(2, obj.iv);
|
||||
if (obj.key) pbf.writeBytesField(3, obj.key);
|
||||
if (obj.type) pbf.writeVarintField(4, obj.type);
|
||||
if (obj.level != undefined && obj.level !== {"value":1,"options":{}}) pbf.writeVarintField(5, obj.level);
|
||||
if (obj.required_protection) pbf.writeMessage(6, License.KeyContainer.OutputProtection.write, obj.required_protection);
|
||||
if (obj.requested_protection) pbf.writeMessage(7, License.KeyContainer.OutputProtection.write, obj.requested_protection);
|
||||
if (obj.key_control) pbf.writeMessage(8, License.KeyContainer.KeyControl.write, obj.key_control);
|
||||
if (obj.operator_session_key_permissions) pbf.writeMessage(9, License.KeyContainer.OperatorSessionKeyPermissions.write, obj.operator_session_key_permissions);
|
||||
if (obj.video_resolution_constraints) for (var i = 0; i < obj.video_resolution_constraints.length; i++) pbf.writeMessage(10, License.KeyContainer.VideoResolutionConstraint.write, obj.video_resolution_constraints[i]);
|
||||
if (obj.anti_rollback_usage_table) pbf.writeBooleanField(11, obj.anti_rollback_usage_table);
|
||||
};
|
||||
|
||||
License.KeyContainer.KeyType = {
|
||||
"SIGNING": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"CONTENT": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"KEY_CONTROL": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
},
|
||||
"OPERATOR_SESSION": {
|
||||
"value": 4,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
License.KeyContainer.SecurityLevel = {
|
||||
"SW_SECURE_CRYPTO": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"SW_SECURE_DECODE": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"HW_SECURE_CRYPTO": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
},
|
||||
"HW_SECURE_DECODE": {
|
||||
"value": 4,
|
||||
"options": {}
|
||||
},
|
||||
"HW_SECURE_ALL": {
|
||||
"value": 5,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// License.KeyContainer.KeyControl ========================================
|
||||
|
||||
License.KeyContainer.KeyControl = {};
|
||||
|
||||
License.KeyContainer.KeyControl.read = function (pbf, end) {
|
||||
return pbf.readFields(License.KeyContainer.KeyControl._readField, {key_control_block: null, iv: null}, end);
|
||||
};
|
||||
License.KeyContainer.KeyControl._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.key_control_block = pbf.readBytes();
|
||||
else if (tag === 2) obj.iv = pbf.readBytes();
|
||||
};
|
||||
License.KeyContainer.KeyControl.write = function (obj, pbf) {
|
||||
if (obj.key_control_block) pbf.writeBytesField(1, obj.key_control_block);
|
||||
if (obj.iv) pbf.writeBytesField(2, obj.iv);
|
||||
};
|
||||
|
||||
// License.KeyContainer.OutputProtection ========================================
|
||||
|
||||
License.KeyContainer.OutputProtection = {};
|
||||
|
||||
License.KeyContainer.OutputProtection.read = function (pbf, end) {
|
||||
return pbf.readFields(License.KeyContainer.OutputProtection._readField, {hdcp: {"value":0,"options":{}}, cgms_flags: {"value":42,"options":{}}}, end);
|
||||
};
|
||||
License.KeyContainer.OutputProtection._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.hdcp = pbf.readVarint();
|
||||
else if (tag === 2) obj.cgms_flags = pbf.readVarint();
|
||||
};
|
||||
License.KeyContainer.OutputProtection.write = function (obj, pbf) {
|
||||
if (obj.hdcp != undefined && obj.hdcp !== {"value":0,"options":{}}) pbf.writeVarintField(1, obj.hdcp);
|
||||
if (obj.cgms_flags != undefined && obj.cgms_flags !== {"value":42,"options":{}}) pbf.writeVarintField(2, obj.cgms_flags);
|
||||
};
|
||||
|
||||
License.KeyContainer.OutputProtection.HDCP = {
|
||||
"HDCP_NONE": {
|
||||
"value": 0,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_V1": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_V2": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_V2_1": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_V2_2": {
|
||||
"value": 4,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_NO_DIGITAL_OUTPUT": {
|
||||
"value": 255,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
License.KeyContainer.OutputProtection.CGMS = {
|
||||
"CGMS_NONE": {
|
||||
"value": 42,
|
||||
"options": {}
|
||||
},
|
||||
"COPY_FREE": {
|
||||
"value": 0,
|
||||
"options": {}
|
||||
},
|
||||
"COPY_ONCE": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"COPY_NEVER": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// License.KeyContainer.VideoResolutionConstraint ========================================
|
||||
|
||||
License.KeyContainer.VideoResolutionConstraint = {};
|
||||
|
||||
License.KeyContainer.VideoResolutionConstraint.read = function (pbf, end) {
|
||||
return pbf.readFields(License.KeyContainer.VideoResolutionConstraint._readField, {min_resolution_pixels: 0, max_resolution_pixels: 0, required_protection: null}, end);
|
||||
};
|
||||
License.KeyContainer.VideoResolutionConstraint._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.min_resolution_pixels = pbf.readVarint();
|
||||
else if (tag === 2) obj.max_resolution_pixels = pbf.readVarint();
|
||||
else if (tag === 3) obj.required_protection = License.KeyContainer.OutputProtection.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
};
|
||||
License.KeyContainer.VideoResolutionConstraint.write = function (obj, pbf) {
|
||||
if (obj.min_resolution_pixels) pbf.writeVarintField(1, obj.min_resolution_pixels);
|
||||
if (obj.max_resolution_pixels) pbf.writeVarintField(2, obj.max_resolution_pixels);
|
||||
if (obj.required_protection) pbf.writeMessage(3, License.KeyContainer.OutputProtection.write, obj.required_protection);
|
||||
};
|
||||
|
||||
// License.KeyContainer.OperatorSessionKeyPermissions ========================================
|
||||
|
||||
License.KeyContainer.OperatorSessionKeyPermissions = {};
|
||||
|
||||
License.KeyContainer.OperatorSessionKeyPermissions.read = function (pbf, end) {
|
||||
return pbf.readFields(License.KeyContainer.OperatorSessionKeyPermissions._readField, {allow_encrypt: false, allow_decrypt: false, allow_sign: false, allow_signature_verify: false}, end);
|
||||
};
|
||||
License.KeyContainer.OperatorSessionKeyPermissions._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.allow_encrypt = pbf.readBoolean();
|
||||
else if (tag === 2) obj.allow_decrypt = pbf.readBoolean();
|
||||
else if (tag === 3) obj.allow_sign = pbf.readBoolean();
|
||||
else if (tag === 4) obj.allow_signature_verify = pbf.readBoolean();
|
||||
};
|
||||
License.KeyContainer.OperatorSessionKeyPermissions.write = function (obj, pbf) {
|
||||
if (obj.allow_encrypt) pbf.writeBooleanField(1, obj.allow_encrypt);
|
||||
if (obj.allow_decrypt) pbf.writeBooleanField(2, obj.allow_decrypt);
|
||||
if (obj.allow_sign) pbf.writeBooleanField(3, obj.allow_sign);
|
||||
if (obj.allow_signature_verify) pbf.writeBooleanField(4, obj.allow_signature_verify);
|
||||
};
|
||||
|
||||
// LicenseRequest ========================================
|
||||
|
||||
var LicenseRequest = self.LicenseRequest = {};
|
||||
|
||||
LicenseRequest.read = function (pbf, end) {
|
||||
return pbf.readFields(LicenseRequest._readField, {client_id: null, content_id: null, type: 0, request_time: 0, key_control_nonce_deprecated: null, protocol_version: {"value":20,"options":{}}, key_control_nonce: 0, encrypted_client_id: null}, end);
|
||||
};
|
||||
LicenseRequest._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.client_id = ClientIdentification.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 2) obj.content_id = LicenseRequest.ContentIdentification.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 3) obj.type = pbf.readVarint();
|
||||
else if (tag === 4) obj.request_time = pbf.readVarint(true);
|
||||
else if (tag === 5) obj.key_control_nonce_deprecated = pbf.readBytes();
|
||||
else if (tag === 6) obj.protocol_version = pbf.readVarint();
|
||||
else if (tag === 7) obj.key_control_nonce = pbf.readVarint();
|
||||
else if (tag === 8) obj.encrypted_client_id = EncryptedClientIdentification.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
};
|
||||
LicenseRequest.write = function (obj, pbf) {
|
||||
if (obj.client_id) pbf.writeMessage(1, ClientIdentification.write, obj.client_id);
|
||||
if (obj.content_id) pbf.writeMessage(2, LicenseRequest.ContentIdentification.write, obj.content_id);
|
||||
if (obj.type) pbf.writeVarintField(3, obj.type);
|
||||
if (obj.request_time) pbf.writeVarintField(4, obj.request_time);
|
||||
if (obj.key_control_nonce_deprecated) pbf.writeBytesField(5, obj.key_control_nonce_deprecated);
|
||||
if (obj.protocol_version != undefined && obj.protocol_version !== {"value":20,"options":{}}) pbf.writeVarintField(6, obj.protocol_version);
|
||||
if (obj.key_control_nonce) pbf.writeVarintField(7, obj.key_control_nonce);
|
||||
if (obj.encrypted_client_id) pbf.writeMessage(8, EncryptedClientIdentification.write, obj.encrypted_client_id);
|
||||
};
|
||||
|
||||
LicenseRequest.RequestType = {
|
||||
"NEW": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"RENEWAL": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"RELEASE": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// LicenseRequest.ContentIdentification ========================================
|
||||
|
||||
LicenseRequest.ContentIdentification = {};
|
||||
|
||||
LicenseRequest.ContentIdentification.read = function (pbf, end) {
|
||||
return pbf.readFields(LicenseRequest.ContentIdentification._readField, {cenc_id: null, webm_id: null, license: null}, end);
|
||||
};
|
||||
LicenseRequest.ContentIdentification._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.cenc_id = LicenseRequest.ContentIdentification.CENC.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 2) obj.webm_id = LicenseRequest.ContentIdentification.WebM.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 3) obj.license = LicenseRequest.ContentIdentification.ExistingLicense.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
};
|
||||
LicenseRequest.ContentIdentification.write = function (obj, pbf) {
|
||||
if (obj.cenc_id) pbf.writeMessage(1, LicenseRequest.ContentIdentification.CENC.write, obj.cenc_id);
|
||||
if (obj.webm_id) pbf.writeMessage(2, LicenseRequest.ContentIdentification.WebM.write, obj.webm_id);
|
||||
if (obj.license) pbf.writeMessage(3, LicenseRequest.ContentIdentification.ExistingLicense.write, obj.license);
|
||||
};
|
||||
|
||||
// LicenseRequest.ContentIdentification.CENC ========================================
|
||||
|
||||
LicenseRequest.ContentIdentification.CENC = {};
|
||||
|
||||
LicenseRequest.ContentIdentification.CENC.read = function (pbf, end) {
|
||||
return pbf.readFields(LicenseRequest.ContentIdentification.CENC._readField, {pssh: [], license_type: 0, request_id: null}, end);
|
||||
};
|
||||
LicenseRequest.ContentIdentification.CENC._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.pssh.push(pbf.readBytes());
|
||||
else if (tag === 2) obj.license_type = pbf.readVarint();
|
||||
else if (tag === 3) obj.request_id = pbf.readBytes();
|
||||
};
|
||||
LicenseRequest.ContentIdentification.CENC.write = function (obj, pbf) {
|
||||
if (obj.pssh) for (var i = 0; i < obj.pssh.length; i++) pbf.writeBytesField(1, obj.pssh[i]);
|
||||
if (obj.license_type) pbf.writeVarintField(2, obj.license_type);
|
||||
if (obj.request_id) pbf.writeBytesField(3, obj.request_id);
|
||||
};
|
||||
|
||||
// LicenseRequest.ContentIdentification.WebM ========================================
|
||||
|
||||
LicenseRequest.ContentIdentification.WebM = {};
|
||||
|
||||
LicenseRequest.ContentIdentification.WebM.read = function (pbf, end) {
|
||||
return pbf.readFields(LicenseRequest.ContentIdentification.WebM._readField, {header: null, license_type: 0, request_id: null}, end);
|
||||
};
|
||||
LicenseRequest.ContentIdentification.WebM._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.header = pbf.readBytes();
|
||||
else if (tag === 2) obj.license_type = pbf.readVarint();
|
||||
else if (tag === 3) obj.request_id = pbf.readBytes();
|
||||
};
|
||||
LicenseRequest.ContentIdentification.WebM.write = function (obj, pbf) {
|
||||
if (obj.header) pbf.writeBytesField(1, obj.header);
|
||||
if (obj.license_type) pbf.writeVarintField(2, obj.license_type);
|
||||
if (obj.request_id) pbf.writeBytesField(3, obj.request_id);
|
||||
};
|
||||
|
||||
// LicenseRequest.ContentIdentification.ExistingLicense ========================================
|
||||
|
||||
LicenseRequest.ContentIdentification.ExistingLicense = {};
|
||||
|
||||
LicenseRequest.ContentIdentification.ExistingLicense.read = function (pbf, end) {
|
||||
return pbf.readFields(LicenseRequest.ContentIdentification.ExistingLicense._readField, {license_id: null, seconds_since_started: 0, seconds_since_last_played: 0, session_usage_table_entry: null}, end);
|
||||
};
|
||||
LicenseRequest.ContentIdentification.ExistingLicense._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.license_id = LicenseIdentification.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 2) obj.seconds_since_started = pbf.readVarint(true);
|
||||
else if (tag === 3) obj.seconds_since_last_played = pbf.readVarint(true);
|
||||
else if (tag === 4) obj.session_usage_table_entry = pbf.readBytes();
|
||||
};
|
||||
LicenseRequest.ContentIdentification.ExistingLicense.write = function (obj, pbf) {
|
||||
if (obj.license_id) pbf.writeMessage(1, LicenseIdentification.write, obj.license_id);
|
||||
if (obj.seconds_since_started) pbf.writeVarintField(2, obj.seconds_since_started);
|
||||
if (obj.seconds_since_last_played) pbf.writeVarintField(3, obj.seconds_since_last_played);
|
||||
if (obj.session_usage_table_entry) pbf.writeBytesField(4, obj.session_usage_table_entry);
|
||||
};
|
||||
|
||||
// LicenseError ========================================
|
||||
|
||||
var LicenseError = self.LicenseError = {};
|
||||
|
||||
LicenseError.read = function (pbf, end) {
|
||||
return pbf.readFields(LicenseError._readField, {error_code: 0}, end);
|
||||
};
|
||||
LicenseError._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.error_code = pbf.readVarint();
|
||||
};
|
||||
LicenseError.write = function (obj, pbf) {
|
||||
if (obj.error_code) pbf.writeVarintField(1, obj.error_code);
|
||||
};
|
||||
|
||||
LicenseError.Error = {
|
||||
"INVALID_DEVICE_CERTIFICATE": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"REVOKED_DEVICE_CERTIFICATE": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"SERVICE_UNAVAILABLE": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// RemoteAttestation ========================================
|
||||
|
||||
var RemoteAttestation = self.RemoteAttestation = {};
|
||||
|
||||
RemoteAttestation.read = function (pbf, end) {
|
||||
return pbf.readFields(RemoteAttestation._readField, {certificate: null, salt: null, signature: null}, end);
|
||||
};
|
||||
RemoteAttestation._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.certificate = EncryptedClientIdentification.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 2) obj.salt = pbf.readBytes();
|
||||
else if (tag === 3) obj.signature = pbf.readBytes();
|
||||
};
|
||||
RemoteAttestation.write = function (obj, pbf) {
|
||||
if (obj.certificate) pbf.writeMessage(1, EncryptedClientIdentification.write, obj.certificate);
|
||||
if (obj.salt) pbf.writeBytesField(2, obj.salt);
|
||||
if (obj.signature) pbf.writeBytesField(3, obj.signature);
|
||||
};
|
||||
|
||||
// SignedMessage ========================================
|
||||
|
||||
var SignedMessage = self.SignedMessage = {};
|
||||
|
||||
SignedMessage.read = function (pbf, end) {
|
||||
return pbf.readFields(SignedMessage._readField, {type: 0, msg: null, signature: null, session_key: null, remote_attestation: null}, end);
|
||||
};
|
||||
SignedMessage._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.type = pbf.readVarint();
|
||||
else if (tag === 2) obj.msg = pbf.readBytes();
|
||||
else if (tag === 3) obj.signature = pbf.readBytes();
|
||||
else if (tag === 4) obj.session_key = pbf.readBytes();
|
||||
else if (tag === 5) obj.remote_attestation = RemoteAttestation.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
};
|
||||
SignedMessage.write = function (obj, pbf) {
|
||||
if (obj.type) pbf.writeVarintField(1, obj.type);
|
||||
if (obj.msg) pbf.writeBytesField(2, obj.msg);
|
||||
if (obj.signature) pbf.writeBytesField(3, obj.signature);
|
||||
if (obj.session_key) pbf.writeBytesField(4, obj.session_key);
|
||||
if (obj.remote_attestation) pbf.writeMessage(5, RemoteAttestation.write, obj.remote_attestation);
|
||||
};
|
||||
|
||||
SignedMessage.MessageType = {
|
||||
"LICENSE_REQUEST": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"LICENSE": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"ERROR_RESPONSE": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
},
|
||||
"SERVICE_CERTIFICATE_REQUEST": {
|
||||
"value": 4,
|
||||
"options": {}
|
||||
},
|
||||
"SERVICE_CERTIFICATE": {
|
||||
"value": 5,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// ProvisioningOptions ========================================
|
||||
|
||||
var ProvisioningOptions = self.ProvisioningOptions = {};
|
||||
|
||||
ProvisioningOptions.read = function (pbf, end) {
|
||||
return pbf.readFields(ProvisioningOptions._readField, {certificate_type: 0, certificate_authority: ""}, end);
|
||||
};
|
||||
ProvisioningOptions._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.certificate_type = pbf.readVarint();
|
||||
else if (tag === 2) obj.certificate_authority = pbf.readString();
|
||||
};
|
||||
ProvisioningOptions.write = function (obj, pbf) {
|
||||
if (obj.certificate_type) pbf.writeVarintField(1, obj.certificate_type);
|
||||
if (obj.certificate_authority) pbf.writeStringField(2, obj.certificate_authority);
|
||||
};
|
||||
|
||||
ProvisioningOptions.CertificateType = {
|
||||
"WIDEVINE_DRM": {
|
||||
"value": 0,
|
||||
"options": {}
|
||||
},
|
||||
"X509": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// ProvisioningRequest ========================================
|
||||
|
||||
var ProvisioningRequest = self.ProvisioningRequest = {};
|
||||
|
||||
ProvisioningRequest.read = function (pbf, end) {
|
||||
return pbf.readFields(ProvisioningRequest._readField, {client_id: null, nonce: null, options: null, stable_id: null}, end);
|
||||
};
|
||||
ProvisioningRequest._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.client_id = ClientIdentification.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 2) obj.nonce = pbf.readBytes();
|
||||
else if (tag === 3) obj.options = ProvisioningOptions.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
else if (tag === 4) obj.stable_id = pbf.readBytes();
|
||||
};
|
||||
ProvisioningRequest.write = function (obj, pbf) {
|
||||
if (obj.client_id) pbf.writeMessage(1, ClientIdentification.write, obj.client_id);
|
||||
if (obj.nonce) pbf.writeBytesField(2, obj.nonce);
|
||||
if (obj.options) pbf.writeMessage(3, ProvisioningOptions.write, obj.options);
|
||||
if (obj.stable_id) pbf.writeBytesField(4, obj.stable_id);
|
||||
};
|
||||
|
||||
// ProvisioningResponse ========================================
|
||||
|
||||
var ProvisioningResponse = self.ProvisioningResponse = {};
|
||||
|
||||
ProvisioningResponse.read = function (pbf, end) {
|
||||
return pbf.readFields(ProvisioningResponse._readField, {device_rsa_key: null, device_rsa_key_iv: null, device_certificate: null, nonce: null}, end);
|
||||
};
|
||||
ProvisioningResponse._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.device_rsa_key = pbf.readBytes();
|
||||
else if (tag === 2) obj.device_rsa_key_iv = pbf.readBytes();
|
||||
else if (tag === 3) obj.device_certificate = pbf.readBytes();
|
||||
else if (tag === 4) obj.nonce = pbf.readBytes();
|
||||
};
|
||||
ProvisioningResponse.write = function (obj, pbf) {
|
||||
if (obj.device_rsa_key) pbf.writeBytesField(1, obj.device_rsa_key);
|
||||
if (obj.device_rsa_key_iv) pbf.writeBytesField(2, obj.device_rsa_key_iv);
|
||||
if (obj.device_certificate) pbf.writeBytesField(3, obj.device_certificate);
|
||||
if (obj.nonce) pbf.writeBytesField(4, obj.nonce);
|
||||
};
|
||||
|
||||
// SignedProvisioningMessage ========================================
|
||||
|
||||
var SignedProvisioningMessage = self.SignedProvisioningMessage = {};
|
||||
|
||||
SignedProvisioningMessage.read = function (pbf, end) {
|
||||
return pbf.readFields(SignedProvisioningMessage._readField, {message: null, signature: null}, end);
|
||||
};
|
||||
SignedProvisioningMessage._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.message = pbf.readBytes();
|
||||
else if (tag === 2) obj.signature = pbf.readBytes();
|
||||
};
|
||||
SignedProvisioningMessage.write = function (obj, pbf) {
|
||||
if (obj.message) pbf.writeBytesField(1, obj.message);
|
||||
if (obj.signature) pbf.writeBytesField(2, obj.signature);
|
||||
};
|
||||
|
||||
// ClientIdentification ========================================
|
||||
|
||||
var ClientIdentification = self.ClientIdentification = {};
|
||||
|
||||
ClientIdentification.read = function (pbf, end) {
|
||||
return pbf.readFields(ClientIdentification._readField, {type: {"value":0,"options":{}}, token: null, client_info: [], provider_client_token: null, license_counter: 0, client_capabilities: null}, end);
|
||||
};
|
||||
ClientIdentification._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.type = pbf.readVarint();
|
||||
else if (tag === 2) obj.token = pbf.readBytes();
|
||||
else if (tag === 3) obj.client_info.push(ClientIdentification.NameValue.read(pbf, pbf.readVarint() + pbf.pos));
|
||||
else if (tag === 4) obj.provider_client_token = pbf.readBytes();
|
||||
else if (tag === 5) obj.license_counter = pbf.readVarint();
|
||||
else if (tag === 6) obj.client_capabilities = ClientIdentification.ClientCapabilities.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
};
|
||||
ClientIdentification.write = function (obj, pbf) {
|
||||
if (obj.type != undefined && obj.type !== {"value":0,"options":{}}) pbf.writeVarintField(1, obj.type);
|
||||
if (obj.token) pbf.writeBytesField(2, obj.token);
|
||||
if (obj.client_info) for (var i = 0; i < obj.client_info.length; i++) pbf.writeMessage(3, ClientIdentification.NameValue.write, obj.client_info[i]);
|
||||
if (obj.provider_client_token) pbf.writeBytesField(4, obj.provider_client_token);
|
||||
if (obj.license_counter) pbf.writeVarintField(5, obj.license_counter);
|
||||
if (obj.client_capabilities) pbf.writeMessage(6, ClientIdentification.ClientCapabilities.write, obj.client_capabilities);
|
||||
};
|
||||
|
||||
ClientIdentification.TokenType = {
|
||||
"KEYBOX": {
|
||||
"value": 0,
|
||||
"options": {}
|
||||
},
|
||||
"DEVICE_CERTIFICATE": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"REMOTE_ATTESTATION_CERTIFICATE": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// ClientIdentification.NameValue ========================================
|
||||
|
||||
ClientIdentification.NameValue = {};
|
||||
|
||||
ClientIdentification.NameValue.read = function (pbf, end) {
|
||||
return pbf.readFields(ClientIdentification.NameValue._readField, {name: "", value: ""}, end);
|
||||
};
|
||||
ClientIdentification.NameValue._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.name = pbf.readString();
|
||||
else if (tag === 2) obj.value = pbf.readString();
|
||||
};
|
||||
ClientIdentification.NameValue.write = function (obj, pbf) {
|
||||
if (obj.name) pbf.writeStringField(1, obj.name);
|
||||
if (obj.value) pbf.writeStringField(2, obj.value);
|
||||
};
|
||||
|
||||
// ClientIdentification.ClientCapabilities ========================================
|
||||
|
||||
ClientIdentification.ClientCapabilities = {};
|
||||
|
||||
ClientIdentification.ClientCapabilities.read = function (pbf, end) {
|
||||
return pbf.readFields(ClientIdentification.ClientCapabilities._readField, {client_token: false, session_token: false, video_resolution_constraints: false, max_hdcp_version: {"value":0,"options":{}}, oem_crypto_api_version: 0, anti_rollback_usage_table: false}, end);
|
||||
};
|
||||
ClientIdentification.ClientCapabilities._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.client_token = pbf.readBoolean();
|
||||
else if (tag === 2) obj.session_token = pbf.readBoolean();
|
||||
else if (tag === 3) obj.video_resolution_constraints = pbf.readBoolean();
|
||||
else if (tag === 4) obj.max_hdcp_version = pbf.readVarint();
|
||||
else if (tag === 5) obj.oem_crypto_api_version = pbf.readVarint();
|
||||
else if (tag === 6) obj.anti_rollback_usage_table = pbf.readBoolean();
|
||||
};
|
||||
ClientIdentification.ClientCapabilities.write = function (obj, pbf) {
|
||||
if (obj.client_token) pbf.writeBooleanField(1, obj.client_token);
|
||||
if (obj.session_token) pbf.writeBooleanField(2, obj.session_token);
|
||||
if (obj.video_resolution_constraints) pbf.writeBooleanField(3, obj.video_resolution_constraints);
|
||||
if (obj.max_hdcp_version != undefined && obj.max_hdcp_version !== {"value":0,"options":{}}) pbf.writeVarintField(4, obj.max_hdcp_version);
|
||||
if (obj.oem_crypto_api_version) pbf.writeVarintField(5, obj.oem_crypto_api_version);
|
||||
if (obj.anti_rollback_usage_table) pbf.writeBooleanField(6, obj.anti_rollback_usage_table);
|
||||
};
|
||||
|
||||
ClientIdentification.ClientCapabilities.HdcpVersion = {
|
||||
"HDCP_NONE": {
|
||||
"value": 0,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_V1": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_V2": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_V2_1": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_V2_2": {
|
||||
"value": 4,
|
||||
"options": {}
|
||||
},
|
||||
"HDCP_NO_DIGITAL_OUTPUT": {
|
||||
"value": 255,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// EncryptedClientIdentification ========================================
|
||||
|
||||
var EncryptedClientIdentification = self.EncryptedClientIdentification = {};
|
||||
|
||||
EncryptedClientIdentification.read = function (pbf, end) {
|
||||
return pbf.readFields(EncryptedClientIdentification._readField, {service_id: "", service_certificate_serial_number: null, encrypted_client_id: null, encrypted_client_id_iv: null, encrypted_privacy_key: null}, end);
|
||||
};
|
||||
EncryptedClientIdentification._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.service_id = pbf.readString();
|
||||
else if (tag === 2) obj.service_certificate_serial_number = pbf.readBytes();
|
||||
else if (tag === 3) obj.encrypted_client_id = pbf.readBytes();
|
||||
else if (tag === 4) obj.encrypted_client_id_iv = pbf.readBytes();
|
||||
else if (tag === 5) obj.encrypted_privacy_key = pbf.readBytes();
|
||||
};
|
||||
EncryptedClientIdentification.write = function (obj, pbf) {
|
||||
if (obj.service_id) pbf.writeStringField(1, obj.service_id);
|
||||
if (obj.service_certificate_serial_number) pbf.writeBytesField(2, obj.service_certificate_serial_number);
|
||||
if (obj.encrypted_client_id) pbf.writeBytesField(3, obj.encrypted_client_id);
|
||||
if (obj.encrypted_client_id_iv) pbf.writeBytesField(4, obj.encrypted_client_id_iv);
|
||||
if (obj.encrypted_privacy_key) pbf.writeBytesField(5, obj.encrypted_privacy_key);
|
||||
};
|
||||
|
||||
// DeviceCertificate ========================================
|
||||
|
||||
var DeviceCertificate = self.DeviceCertificate = {};
|
||||
|
||||
DeviceCertificate.read = function (pbf, end) {
|
||||
return pbf.readFields(DeviceCertificate._readField, {type: 0, serial_number: null, creation_time_seconds: 0, public_key: null, system_id: 0, test_device_deprecated: false, service_id: ""}, end);
|
||||
};
|
||||
DeviceCertificate._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.type = pbf.readVarint();
|
||||
else if (tag === 2) obj.serial_number = pbf.readBytes();
|
||||
else if (tag === 3) obj.creation_time_seconds = pbf.readVarint();
|
||||
else if (tag === 4) obj.public_key = pbf.readBytes();
|
||||
else if (tag === 5) obj.system_id = pbf.readVarint();
|
||||
else if (tag === 6) obj.test_device_deprecated = pbf.readBoolean();
|
||||
else if (tag === 7) obj.service_id = pbf.readString();
|
||||
};
|
||||
DeviceCertificate.write = function (obj, pbf) {
|
||||
if (obj.type) pbf.writeVarintField(1, obj.type);
|
||||
if (obj.serial_number) pbf.writeBytesField(2, obj.serial_number);
|
||||
if (obj.creation_time_seconds) pbf.writeVarintField(3, obj.creation_time_seconds);
|
||||
if (obj.public_key) pbf.writeBytesField(4, obj.public_key);
|
||||
if (obj.system_id) pbf.writeVarintField(5, obj.system_id);
|
||||
if (obj.test_device_deprecated) pbf.writeBooleanField(6, obj.test_device_deprecated);
|
||||
if (obj.service_id) pbf.writeStringField(7, obj.service_id);
|
||||
};
|
||||
|
||||
DeviceCertificate.CertificateType = {
|
||||
"ROOT": {
|
||||
"value": 0,
|
||||
"options": {}
|
||||
},
|
||||
"INTERMEDIATE": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"USER_DEVICE": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"SERVICE": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// SignedDeviceCertificate ========================================
|
||||
|
||||
var SignedDeviceCertificate = self.SignedDeviceCertificate = {};
|
||||
|
||||
SignedDeviceCertificate.read = function (pbf, end) {
|
||||
return pbf.readFields(SignedDeviceCertificate._readField, {device_certificate: null, signature: null, signer: null}, end);
|
||||
};
|
||||
SignedDeviceCertificate._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.device_certificate = pbf.readBytes();
|
||||
else if (tag === 2) obj.signature = pbf.readBytes();
|
||||
else if (tag === 3) obj.signer = SignedDeviceCertificate.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
};
|
||||
SignedDeviceCertificate.write = function (obj, pbf) {
|
||||
if (obj.device_certificate) pbf.writeBytesField(1, obj.device_certificate);
|
||||
if (obj.signature) pbf.writeBytesField(2, obj.signature);
|
||||
if (obj.signer) pbf.writeMessage(3, SignedDeviceCertificate.write, obj.signer);
|
||||
};
|
||||
|
||||
// ProvisionedDeviceInfo ========================================
|
||||
|
||||
var ProvisionedDeviceInfo = self.ProvisionedDeviceInfo = {};
|
||||
|
||||
ProvisionedDeviceInfo.read = function (pbf, end) {
|
||||
return pbf.readFields(ProvisionedDeviceInfo._readField, {system_id: 0, soc: "", manufacturer: "", model: "", device_type: "", model_year: 0, security_level: {"value":0,"options":{}}, test_device: false}, end);
|
||||
};
|
||||
ProvisionedDeviceInfo._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.system_id = pbf.readVarint();
|
||||
else if (tag === 2) obj.soc = pbf.readString();
|
||||
else if (tag === 3) obj.manufacturer = pbf.readString();
|
||||
else if (tag === 4) obj.model = pbf.readString();
|
||||
else if (tag === 5) obj.device_type = pbf.readString();
|
||||
else if (tag === 6) obj.model_year = pbf.readVarint();
|
||||
else if (tag === 7) obj.security_level = pbf.readVarint();
|
||||
else if (tag === 8) obj.test_device = pbf.readBoolean();
|
||||
};
|
||||
ProvisionedDeviceInfo.write = function (obj, pbf) {
|
||||
if (obj.system_id) pbf.writeVarintField(1, obj.system_id);
|
||||
if (obj.soc) pbf.writeStringField(2, obj.soc);
|
||||
if (obj.manufacturer) pbf.writeStringField(3, obj.manufacturer);
|
||||
if (obj.model) pbf.writeStringField(4, obj.model);
|
||||
if (obj.device_type) pbf.writeStringField(5, obj.device_type);
|
||||
if (obj.model_year) pbf.writeVarintField(6, obj.model_year);
|
||||
if (obj.security_level != undefined && obj.security_level !== {"value":0,"options":{}}) pbf.writeVarintField(7, obj.security_level);
|
||||
if (obj.test_device) pbf.writeBooleanField(8, obj.test_device);
|
||||
};
|
||||
|
||||
ProvisionedDeviceInfo.WvSecurityLevel = {
|
||||
"LEVEL_UNSPECIFIED": {
|
||||
"value": 0,
|
||||
"options": {}
|
||||
},
|
||||
"LEVEL_1": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
},
|
||||
"LEVEL_2": {
|
||||
"value": 2,
|
||||
"options": {}
|
||||
},
|
||||
"LEVEL_3": {
|
||||
"value": 3,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// DeviceCertificateStatus ========================================
|
||||
|
||||
var DeviceCertificateStatus = self.DeviceCertificateStatus = {};
|
||||
|
||||
DeviceCertificateStatus.read = function (pbf, end) {
|
||||
return pbf.readFields(DeviceCertificateStatus._readField, {serial_number: null, status: {"value":0,"options":{}}, device_info: null}, end);
|
||||
};
|
||||
DeviceCertificateStatus._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.serial_number = pbf.readBytes();
|
||||
else if (tag === 2) obj.status = pbf.readVarint();
|
||||
else if (tag === 4) obj.device_info = ProvisionedDeviceInfo.read(pbf, pbf.readVarint() + pbf.pos);
|
||||
};
|
||||
DeviceCertificateStatus.write = function (obj, pbf) {
|
||||
if (obj.serial_number) pbf.writeBytesField(1, obj.serial_number);
|
||||
if (obj.status != undefined && obj.status !== {"value":0,"options":{}}) pbf.writeVarintField(2, obj.status);
|
||||
if (obj.device_info) pbf.writeMessage(4, ProvisionedDeviceInfo.write, obj.device_info);
|
||||
};
|
||||
|
||||
DeviceCertificateStatus.CertificateStatus = {
|
||||
"VALID": {
|
||||
"value": 0,
|
||||
"options": {}
|
||||
},
|
||||
"REVOKED": {
|
||||
"value": 1,
|
||||
"options": {}
|
||||
}
|
||||
};
|
||||
|
||||
// DeviceCertificateStatusList ========================================
|
||||
|
||||
var DeviceCertificateStatusList = self.DeviceCertificateStatusList = {};
|
||||
|
||||
DeviceCertificateStatusList.read = function (pbf, end) {
|
||||
return pbf.readFields(DeviceCertificateStatusList._readField, {creation_time_seconds: 0, certificate_status: []}, end);
|
||||
};
|
||||
DeviceCertificateStatusList._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.creation_time_seconds = pbf.readVarint();
|
||||
else if (tag === 2) obj.certificate_status.push(DeviceCertificateStatus.read(pbf, pbf.readVarint() + pbf.pos));
|
||||
};
|
||||
DeviceCertificateStatusList.write = function (obj, pbf) {
|
||||
if (obj.creation_time_seconds) pbf.writeVarintField(1, obj.creation_time_seconds);
|
||||
if (obj.certificate_status) for (var i = 0; i < obj.certificate_status.length; i++) pbf.writeMessage(2, DeviceCertificateStatus.write, obj.certificate_status[i]);
|
||||
};
|
||||
|
||||
// SignedCertificateStatusList ========================================
|
||||
|
||||
var SignedCertificateStatusList = self.SignedCertificateStatusList = {};
|
||||
|
||||
SignedCertificateStatusList.read = function (pbf, end) {
|
||||
return pbf.readFields(SignedCertificateStatusList._readField, {certificate_status_list: null, signature: null}, end);
|
||||
};
|
||||
SignedCertificateStatusList._readField = function (tag, obj, pbf) {
|
||||
if (tag === 1) obj.certificate_status_list = pbf.readBytes();
|
||||
else if (tag === 2) obj.signature = pbf.readBytes();
|
||||
};
|
||||
SignedCertificateStatusList.write = function (obj, pbf) {
|
||||
if (obj.certificate_status_list) pbf.writeBytesField(1, obj.certificate_status_list);
|
||||
if (obj.signature) pbf.writeBytesField(2, obj.signature);
|
||||
};
|
Loading…
Reference in New Issue