multi-downloader-nx_mirror/modules/playready/xmrlicense.ts

228 lines
5.8 KiB
TypeScript

import { Parser } from 'binary-parser';
type ParsedLicense = {
version: number;
rights: string;
length: number;
license: {
length: number;
signature?: {
length: number;
type: string;
value: string;
};
global_container?: {
revocationInfo?: {
version: number;
};
securityLevel?: {
level: number;
};
};
keyMaterial?: {
contentKey?: {
kid: string;
keyType: number;
ciphertype: number;
length: number;
value: Buffer;
};
encryptionKey?: {
curve: number;
length: number;
value: string;
};
auxKeys?: {
count: number;
value: {
location: number;
value: string;
};
};
};
};
};
export class XMRLicenseStructsV2 {
static CONTENT_KEY = new Parser().buffer('kid', { length: 16 }).uint16('keytype').uint16('ciphertype').uint16('length').buffer('value', {
length: 'length'
});
static ECC_KEY = new Parser().uint16('curve').uint16('length').buffer('value', {
length: 'length'
});
static FTLV = new Parser()
.uint16('flags')
.uint16('type')
.uint32('length')
.buffer('value', {
length: function () {
return (this as any).length - 8;
}
});
static AUXILIARY_LOCATIONS = new Parser().uint32('location').buffer('value', { length: 16 });
static AUXILIARY_KEY_OBJECT = new Parser().uint16('count').array('locations', {
length: 'count',
type: XMRLicenseStructsV2.AUXILIARY_LOCATIONS
});
static SIGNATURE = new Parser().uint16('type').uint16('siglength').buffer('signature', {
length: 'siglength'
});
static XMR = new Parser().string('constant', { length: 4, assert: 'XMR\x00' }).int32('version').buffer('rightsid', { length: 16 }).nest('data', {
type: XMRLicenseStructsV2.FTLV
});
}
enum XMRTYPE {
XMR_OUTER_CONTAINER = 0x0001,
XMR_GLOBAL_POLICY_CONTAINER = 0x0002,
XMR_PLAYBACK_POLICY_CONTAINER = 0x0004,
XMR_KEY_MATERIAL_CONTAINER = 0x0009,
XMR_RIGHTS_SETTINGS = 0x000d,
XMR_EMBEDDED_LICENSE_SETTINGS = 0x0033,
XMR_REVOCATION_INFORMATION_VERSION = 0x0032,
XMR_SECURITY_LEVEL = 0x0034,
XMR_CONTENT_KEY_OBJECT = 0x000a,
XMR_ECC_KEY_OBJECT = 0x002a,
XMR_SIGNATURE_OBJECT = 0x000b,
XMR_OUTPUT_LEVEL_RESTRICTION = 0x0005,
XMR_AUXILIARY_KEY_OBJECT = 0x0051,
XMR_EXPIRATION_RESTRICTION = 0x0012,
XMR_ISSUE_DATE = 0x0013,
XMR_EXPLICIT_ANALOG_CONTAINER = 0x0007
}
export class XmrUtil {
public data: Buffer;
public license: ParsedLicense;
constructor(data: Buffer, license: ParsedLicense) {
this.data = data;
this.license = license;
}
static parse(license: Buffer) {
const xmr = XMRLicenseStructsV2.XMR.parse(license);
const parsed_license: ParsedLicense = {
version: xmr.version,
rights: Buffer.from(xmr.rightsid).toString('hex'),
length: license.length,
license: {
length: xmr.data.length
}
};
const container = parsed_license.license;
const data = xmr.data;
let pos = 0;
while (pos < data.length - 16) {
const value = XMRLicenseStructsV2.FTLV.parse(data.value.slice(pos));
// XMR_SIGNATURE_OBJECT
if (value.type === XMRTYPE.XMR_SIGNATURE_OBJECT) {
const signature = XMRLicenseStructsV2.SIGNATURE.parse(value.value);
container.signature = {
length: value.length,
type: signature.type,
value: Buffer.from(signature.signature).toString('hex')
};
}
// XMRTYPE.XMR_GLOBAL_POLICY_CONTAINER
if (value.type === XMRTYPE.XMR_GLOBAL_POLICY_CONTAINER) {
container.global_container = {};
let index = 0;
while (index < value.length - 16) {
const data = XMRLicenseStructsV2.FTLV.parse(value.value.slice(index));
// XMRTYPE.XMR_REVOCATION_INFORMATION_VERSION
if (data.type === XMRTYPE.XMR_REVOCATION_INFORMATION_VERSION) {
container.global_container.revocationInfo = {
version: data.value.readUInt32BE(0)
};
}
// XMRTYPE.XMR_SECURITY_LEVEL
if (data.type === XMRTYPE.XMR_SECURITY_LEVEL) {
container.global_container.securityLevel = {
level: data.value.readUInt16BE(0)
};
}
index += data.length;
}
}
// XMRTYPE.XMR_KEY_MATERIAL_CONTAINER
if (value.type === XMRTYPE.XMR_KEY_MATERIAL_CONTAINER) {
container.keyMaterial = {};
let index = 0;
while (index < value.length - 16) {
const data = XMRLicenseStructsV2.FTLV.parse(value.value.slice(index));
// XMRTYPE.XMR_CONTENT_KEY_OBJECT
if (data.type === XMRTYPE.XMR_CONTENT_KEY_OBJECT) {
const content_key = XMRLicenseStructsV2.CONTENT_KEY.parse(data.value);
container.keyMaterial.contentKey = {
kid: XmrUtil.fixUUID(content_key.kid).toString('hex'),
keyType: content_key.keytype,
ciphertype: content_key.ciphertype,
length: content_key.length,
value: content_key.value
};
}
// XMRTYPE.XMR_ECC_KEY_OBJECT
if (data.type === XMRTYPE.XMR_ECC_KEY_OBJECT) {
const ecc_key = XMRLicenseStructsV2.ECC_KEY.parse(data.value);
container.keyMaterial.encryptionKey = {
curve: ecc_key.curve,
length: ecc_key.length,
value: Buffer.from(ecc_key.value).toString('hex')
};
}
// XMRTYPE.XMR_AUXILIARY_KEY_OBJECT
if (data.type === XMRTYPE.XMR_AUXILIARY_KEY_OBJECT) {
const aux_keys = XMRLicenseStructsV2.AUXILIARY_KEY_OBJECT.parse(data.value);
container.keyMaterial.auxKeys = {
count: aux_keys.count,
value: aux_keys.locations.map((a: any) => {
return {
location: a.location,
value: Buffer.from(a.value).toString('hex')
};
})
};
}
index += data.length;
}
}
pos += value.length;
}
return new XmrUtil(license, parsed_license);
}
static fixUUID(data: Buffer): Buffer {
return Buffer.concat([
Buffer.from(data.subarray(0, 4).reverse()),
Buffer.from(data.subarray(4, 6).reverse()),
Buffer.from(data.subarray(6, 8).reverse()),
data.subarray(8, 16)
]);
}
}