Skip to content

stratumadev/node-playready

Repository files navigation

node-playready

A lightweight NodeJS library for working with Microsoft PlayReady

  • Loading and provisioning device certificates (bgroupcert + zgpriv - PRD v2/v3)
  • Generating PlayReady license acquisition challenges (LA_URL / WRMHEADER)
  • Parsing and decrypting PlayReady license responses (XMR)
  • Unpacking PlayReady V3 PRD device files
  • Inspecting PSSHs for PlayReady WRMHeader structures

Installation

npm:

npm install node-playready

pnpm:

pnpm install node-playready

bun:

bun add node-playready

yarn:

yarn add node-playready

Usage

import { readFileSync } from "fs";
import Playready from "node-playready";

// Read bgroupcert file
const bgroupcert = readFileSync("./bgroupcert.dat");
// Read zgpriv file
const zgpriv = readFileSync("./zgpriv.dat");

// Initialize Playready client
const device = Playready.init(bgroupcert, zgpriv)

// Get Device info
console.log(device.info)

// PSSH found in the MPD manifest
const pssh = Buffer.from(
"AAADfHBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAA1xcAwAAAQABAFIDPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4A
cwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AM
gAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMA
AuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAA
vAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBD
AFQASQBOAEYATwA+ADwASwBJAEQAPgA0AFIAcABsAGIAKwBUAGIATgBFAFMAOAB0AEcAawBOAEYAVwBUAEUASABBAD0APQA8A
C8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AEsATABqADMAUQB6AFEAUAAvAE4AQQA9ADwALwBDAEgARQBDAEsAUwBVAE
0APgA8AEwAQQBfAFUAUgBMAD4AaAB0AHQAcABzADoALwAvAHAAcgBvAGYAZgBpAGMAaQBhAGwAcwBpAHQAZQAuAGsAZQB5AGQ
AZQBsAGkAdgBlAHIAeQAuAG0AZQBkAGkAYQBzAGUAcgB2AGkAYwBlAHMALgB3AGkAbgBkAG8AdwBzAC4AbgBlAHQALwBQAGwA
YQB5AFIAZQBhAGQAeQAvADwALwBMAEEAXwBVAFIATAA+ADwAQwBVAFMAVABPAE0AQQBUAFQAUgBJAEIAVQBUAEUAUwA+ADwAS
QBJAFMAXwBEAFIATQBfAFYARQBSAFMASQBPAE4APgA4AC4AMQAuADIAMwAwADQALgAzADEAPAAvAEkASQBTAF8ARABSAE0AXw
BWAEUAUgBTAEkATwBOAD4APAAvAEMAVQBTAFQATwBNAEEAVABUAFIASQBCAFUAVABFAFMAPgA8AC8ARABBAFQAQQA+ADwALwB
XAFIATQBIAEUAQQBEAEUAUgA+AA==",
  "base64"
);

// Generate Challenge
const challenge = device.generateChallenge(pssh);

// License Server URL
const licenseUrl = "https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:2000)";

// License Request
const response = await fetch(licenseUrl, {
  method: "POST",
  headers: {
    'Content-Type': 'text/xml; charset=UTF-8'
  },
  body: challenge
});

// Check if request was successful
if (response.ok) {
  // Parse license
  const successful = device.parseLicense(Buffer.from(await response.text(), 'utf-8')).length > 0
  console.log(`successful? ${successful ? 'yes' : 'no'}`)
} else {
  console.error('Request failed!')
  console.log(await response.text())
}

Usage with PRD files

WARNING: This function does not reprovision the PRD file, if you have a V3 PRD device please use the unpackV3PRD function and init the client with Playready.init()

import { readFileSync } from "fs";
import Playready from "node-playready";

// Read PRD file
const prd = readFileSync("./device.prd");

// Initialize Playready client
const device = Playready.initPRD(prd)

// Get Device info
console.log(device.info)

// PSSH found in the MPD manifest
const pssh = Buffer.from(
"AAADfHBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAA1xcAwAAAQABAFIDPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4A
cwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AM
gAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMA
AuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAA
vAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBD
AFQASQBOAEYATwA+ADwASwBJAEQAPgA0AFIAcABsAGIAKwBUAGIATgBFAFMAOAB0AEcAawBOAEYAVwBUAEUASABBAD0APQA8A
C8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AEsATABqADMAUQB6AFEAUAAvAE4AQQA9ADwALwBDAEgARQBDAEsAUwBVAE
0APgA8AEwAQQBfAFUAUgBMAD4AaAB0AHQAcABzADoALwAvAHAAcgBvAGYAZgBpAGMAaQBhAGwAcwBpAHQAZQAuAGsAZQB5AGQ
AZQBsAGkAdgBlAHIAeQAuAG0AZQBkAGkAYQBzAGUAcgB2AGkAYwBlAHMALgB3AGkAbgBkAG8AdwBzAC4AbgBlAHQALwBQAGwA
YQB5AFIAZQBhAGQAeQAvADwALwBMAEEAXwBVAFIATAA+ADwAQwBVAFMAVABPAE0AQQBUAFQAUgBJAEIAVQBUAEUAUwA+ADwAS
QBJAFMAXwBEAFIATQBfAFYARQBSAFMASQBPAE4APgA4AC4AMQAuADIAMwAwADQALgAzADEAPAAvAEkASQBTAF8ARABSAE0AXw
BWAEUAUgBTAEkATwBOAD4APAAvAEMAVQBTAFQATwBNAEEAVABUAFIASQBCAFUAVABFAFMAPgA8AC8ARABBAFQAQQA+ADwALwB
XAFIATQBIAEUAQQBEAEUAUgA+AA==",
  "base64"
);

// Generate Challenge
const challenge = device.generateChallenge(pssh);

// License Server URL
const licenseUrl = "https://test.playready.microsoft.com/service/rightsmanager.asmx?cfg=(persist:false,sl:2000)";

// License Request
const response = await fetch(licenseUrl, {
  method: "POST",
  headers: {
    'Content-Type': 'text/xml; charset=UTF-8'
  },
  body: challenge
});

// Check if request was successful
if (response.ok) {
  // Parse license
  const successful = device.parseLicense(Buffer.from(await response.text(), 'utf-8')).length > 0
  console.log(`successful? ${successful ? 'yes' : 'no'}`)
} else {
  console.error('Request failed!')
  console.log(await response.text())
}

unpackV3PRD

Unpacks a PlayReady V3 PRD device file.

This function returns the group certificate and group key needed for Playready.init()

import { readFileSync } from 'fs'
import Playready from 'node-playready'

const prd = readFileSync('./device.prd')

const { bgroupcert, zgpriv } = Playready.unpackV3PRD(prd)

console.log('bgroupcert length:', bgroupcert.length)
console.log('zgpriv length:', zgpriv.length)

// Initialize from PRD
const device = Playready.init(bgroupcert, zgpriv)
console.log(device.info)

containsWRMHeader

Checks if a given Base64 string or raw buffer contains a valid PlayReady WRMHeader.

import Playready from "node-playready";

const pssh = Buffer.from(
"AAADfHBzc2gAAAAAmgTweZhAQoarkuZb4IhflQAAA1xcAwAAAQABAFIDPABXAFIATQBIAEUAQQBEAEUAUgAgAHgAbQBsAG4A
cwA9ACIAaAB0AHQAcAA6AC8ALwBzAGMAaABlAG0AYQBzAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAvAEQAUgBNAC8AM
gAwADAANwAvADAAMwAvAFAAbABhAHkAUgBlAGEAZAB5AEgAZQBhAGQAZQByACIAIAB2AGUAcgBzAGkAbwBuAD0AIgA0AC4AMA
AuADAALgAwACIAPgA8AEQAQQBUAEEAPgA8AFAAUgBPAFQARQBDAFQASQBOAEYATwA+ADwASwBFAFkATABFAE4APgAxADYAPAA
vAEsARQBZAEwARQBOAD4APABBAEwARwBJAEQAPgBBAEUAUwBDAFQAUgA8AC8AQQBMAEcASQBEAD4APAAvAFAAUgBPAFQARQBD
AFQASQBOAEYATwA+ADwASwBJAEQAPgA0AFIAcABsAGIAKwBUAGIATgBFAFMAOAB0AEcAawBOAEYAVwBUAEUASABBAD0APQA8A
C8ASwBJAEQAPgA8AEMASABFAEMASwBTAFUATQA+AEsATABqADMAUQB6AFEAUAAvAE4AQQA9ADwALwBDAEgARQBDAEsAUwBVAE
0APgA8AEwAQQBfAFUAUgBMAD4AaAB0AHQAcABzADoALwAvAHAAcgBvAGYAZgBpAGMAaQBhAGwAcwBpAHQAZQAuAGsAZQB5AGQ
AZQBsAGkAdgBlAHIAeQAuAG0AZQBkAGkAYQBzAGUAcgB2AGkAYwBlAHMALgB3AGkAbgBkAG8AdwBzAC4AbgBlAHQALwBQAGwA
YQB5AFIAZQBhAGQAeQAvADwALwBMAEEAXwBVAFIATAA+ADwAQwBVAFMAVABPAE0AQQBUAFQAUgBJAEIAVQBUAEUAUwA+ADwAS
QBJAFMAXwBEAFIATQBfAFYARQBSAFMASQBPAE4APgA4AC4AMQAuADIAMwAwADQALgAzADEAPAAvAEkASQBTAF8ARABSAE0AXw
BWAEUAUgBTAEkATwBOAD4APAAvAEMAVQBTAFQATwBNAEEAVABUAFIASQBCAFUAVABFAFMAPgA8AC8ARABBAFQAQQA+ADwALwB
XAFIATQBIAEUAQQBEAEUAUgA+AA==",
  "base64"
);

if (Playready.containsWRMHeader(pssh)) {
  console.log("Valid PlayReady WRMHeader detected!");
} else {
  console.log("No WRMHeader found.");
}

About

NodeJS Playready Client

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

 
 
 

Contributors