Harmonic
Axinom Key Service supports direct integration with the Harmonic encoder that provides support for the H.264 and H.265 video codecs.
The integration endpoint is /Harmonic/v2
of the Key Acquisition API.
The previous version of this endpoint, /Harmonic
still exists for backward compatibility. It used a different keyID derivation algorithm, hence the new node is not backward compatible, i.e. even the same content encoded again using the new endpoint will receive different keys.
But for all new scenarios it is recommended to use the v2.
Other limitations of the previous version:
- Missing Multi-key and Key-rotation support
- The attribute
commonEncryptionScheme
of the elementcpix:ContentKey
is not supported (see Encryption Schemes below).
Functionality​
The Client requests one or more content keys. The Key Service generates the necessary keys using the Key Seed model and returns the keys with additional DRM-specific metadata where needed.
Authorization​
This endpoint requires an authorization header - the same as described under the Key Service Management API.
Request/Response Format​
Request​
The Client creates a CPIX document in the format which it desires to get, just without the values for the keys, and sends this document to the Key Service. The Key Service generates the specified keys, adds this information to the document, and returns the CPIX document to the Client.
<?xml version="1.0" ?>
<cpix:CPIX contentId="channel_01" xmlns:cpix="urn:dashif:org:cpix">
<cpix:ContentKeyList>
<cpix:ContentKey kid="af1ed63c-5784-460b-9e51-309dd47b7d9c"/>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem kid="af1ed63c-5784-460b-9e51-309dd47b7d9c" systemId="94ce86fb-07ff-4f43-adb8-93d2fa968ca2"/>
<cpix:DRMSystem kid="af1ed63c-5784-460b-9e51-309dd47b7d9c" systemId="9a04f079-9840-4286-ab92-e65be0885f95"/>
<cpix:DRMSystem kid="af1ed63c-5784-460b-9e51-309dd47b7d9c" systemId="edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"/>
</cpix:DRMSystemList>
<cpix:ContentKeyPeriodList>
<cpix:ContentKeyPeriod start="2023-04-17T00:00:00Z" end="2024-02-17T16:55:00Z" id="keyperiod_1608224040"/>
</cpix:ContentKeyPeriodList>
<cpix:ContentKeyUsageRuleList>
<cpix:ContentKeyUsageRule intendedTrackType="audio" kid="af1ed63c-5784-460b-9e51-309dd47b7d9c">
<cpix:KeyPeriodFilter periodId="keyperiod_1608224040"/>
<cpix:AudioFilter/>
</cpix:ContentKeyUsageRule>
</cpix:ContentKeyUsageRuleList>
</cpix:CPIX>
The table below explains some important elements of the request XML.
Element | Purpose |
---|---|
ContentKeyList | The list of the Key IDs for which the Keys shall be generated. Each key ID is a GUID. |
DRMSystemList | Requested DRM types, for which the PSSH boxes will be delivered. The IDs are as defined by DASH-IF. Axinom Key Service supports FairPlay, PlayReady, and Widevine (listed in the example above in this order). |
ContentKeyPeriodList | List of ContentKeyPeriod elements. |
ContentKeyUsageRuleList | Content key usage rules for each key. It’s a pass-through that will only be returned to the Client. |
Response​
The response is a CPIX document.
<?xml version="1.0" encoding="utf-8"?>
<cpix:CPIX contentId="channel_01" xmlns:cpix="urn:dashif:org:cpix">
<cpix:ContentKeyList>
<cpix:ContentKey kid="8b5a0f76-1725-92b3-07d4-f563ca483b0d" explicitIV="50X3FbzWRUZNwBKVnH7EIQ==">
<cpix:Data>
<Secret xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
<PlainValue>kusV8szSsx5HfjjT3Jt+Ew==</PlainValue>
</Secret>
</cpix:Data>
</cpix:ContentKey>
</cpix:ContentKeyList>
<cpix:DRMSystemList>
<cpix:DRMSystem systemId="9a04f079-9840-4286-ab92-e65be0885f95" kid="8b5a0f76-1725-92b3-07d4-f563ca483b0d">
<cpix:HLSsignallingData playlist="master">I0VYVC1YLVNFU1NJT04tS0VZOk1FVEhPRD1TQU1QTEUtQUVTLFVSST0ic2tkOi8vOGI1YTBmNzYtMTcyNS05MmIzLTA3ZDQtZjU2M2NhNDgzYjBkOkU3NDVGNzE1QkNENjQ1NDY0REMwMTI5NTlDN0VDNDIxIixLRVlGT1JNQVQ9ImNvbS5hcHBsZS5zdHJlYW1pbmdrZXlkZWxpdmVyeSIsS0VZRk9STUFUVkVSU0lPTlM9IjEi</cpix:HLSsignallingData>
<cpix:HLSsignallingData playlist="media">I0VYVC1YLUtFWTpNRVRIT0Q9U0FNUExFLUFFUyxVUkk9InNrZDovLzhiNWEwZjc2LTE3MjUtOTJiMy0wN2Q0LWY1NjNjYTQ4M2IwZDpFNzQ1RjcxNUJDRDY0NTQ2NERDMDEyOTU5QzdFQzQyMSIsS0VZRk9STUFUPSJjb20uYXBwbGUuc3RyZWFtaW5na2V5ZGVsaXZlcnkiLEtFWUZPUk1BVFZFUlNJT05TPSIxIg==</cpix:HLSsignallingData>
</cpix:DRMSystem>
</cpix:DRMSystemList>
<cpix:ContentKeyPeriodList>
<cpix:ContentKeyPeriod end="2024-02-17T16:55:00Z" id="keyperiod_1608224040" start="2023-02-17T16:54:00Z" />
</cpix:ContentKeyPeriodList>
<cpix:ContentKeyUsageRuleList>
<cpix:ContentKeyUsageRule kid="8b5a0f76-1725-92b3-07d4-f563ca483b0d" intendedTrackType="audio">
<cpix:KeyPeriodFilter periodId="keyperiod_1608224040" />
<cpix:AudioFilter />
</cpix:ContentKeyUsageRule>
</cpix:ContentKeyUsageRuleList>
</cpix:CPIX>
The table below explains the key elements of the response XML.
Element | Description |
---|---|
ContentKeyList | A generated Key and an initialization vector (IV) for each Key ID |
DRMSystemList | Additional protection data for each requested DRM system and each key |
ContentKeyPeriodList | List of ContentKeyPeriod elements. |
ContentKeyUsageRuleList | Content key usage rules, the same as in the request. |
For configuring a PlayReady Header "LA_URL" in the returned signalling data, please refer to this guide
Encryption Schemes​
With the /Harmonic/v2
endpoint, the License Service supports an optional attribute commonEncryptionScheme
for each requested key.
Values supported for 'commonEncryptionScheme' and each of the DRM technologies. Value in brackets is the default for a given technology.
Scheme | Widevine | PlayReady | FairPlay |
---|---|---|---|
cenc | (X) | (X) | |
cbcs | X | X | (X) |
cens | X | ||
cbc1 | X |
If you need FairPlay together with Widevine/PlayReady, you have to set commonEncryptionScheme
explicitly to cbcs
. Otherwise, different encryption schemes will be selected by default, breaking the keyID-override algorithm.
For the v1 endpoint, /Harmonic
, the value of this attribute was not considered and the encryption scheme was always set to the defaults in the table above. The keyID-override algorithm didn’t take the encryption scheme into account, so the limitation above did not exist.
KeyID Override​
The keyID (attribute keyID
) from the request CPIX is not used.
...
<cpix:ContentKey kid="af1ed63c-5784-460b-9e51-309dd47b7d9c"/>
...
Instead, the Key Service generates another keyID using a deterministic algorithm based on the provided contentID
. While SPEKE endpoints supports enabling and disabling this feature, Harmonic endpoints will always override the key IDs and cannot be disabled.
Key Derivation for Harmonic V2​
Similar to the SPEKE endpoint, we take all input parameters that contribute to the uniqueness of the Key ID, compute a SHA-256 hash of them, and then XOR the first and second halves of the hash to reduce the output to 128 bits. The resulting 128-bit value is then converted into a GUID. However, note that the input parameters used in the algorithm differ between Harmonic and SPEKE.
Parameters used for Harmonic V2:
-
TenantId - Customer’s Axinom DRM Tenant ID (to be found under My Mosaic / DRM / Key Service configuration).
-
Content ID - The identifier of a video asset or a stream. In the request CPIX, it is the
contentId
attribute of the rootcpix:CPIX
. -
Protection Scheme - The protection scheme used for the content key. This defaults to
cenc
for keys associated with Widevine and PlayReady, and tocbcs
for FairPlay, unless otherwise specified in the CPIX request. Note that if a key is used for both FairPlay and other DRM technologies, the protection scheme will becbcs
due to FairPlay can only use CBCS protection scheme. -
Intended Track Type - The track type of the key ID specified in the packager. This is typically
VIDEO
,AUDIO
,HD
,SD
, etc., but it can be any string value defined by the packager. Please refer to the packager’s documentation for details. If no track type is defined, an empty string may be used. -
Content Key Period Index - When the packager’s key rotation is enabled in Index mode, each CPIX key request includes a sequential period index number. In the Harmonic packager, this is typically a Unix timestamp. When key rotation is disabled, the key derivation algorithm will use an empty string.
-
Content Key Period Start - When the packager’s key rotation is enabled in Timestamp"** mode, this parameter is required for key derivation. It works in conjunction with the Key Period Interval parameter. The value is a Unix epoch timestamp that is floored to the nearest boundary based on the specified key period interval. For example, if the interval is 30 minutes, the key period start times will be precise 30-minute marks such as 01:00:00, 01:30:00, etc. If the interval is 10 minutes, the times would be 01:00:00, 01:10:00, 01:20:00, and so on. Note that this start time used in the algorithm may not align exactly with the actual CPIX request time. However, for practicality, the algorithm is designed to adjust to a predictable and consistent timestamp that is easy to work with.
-
Key Period Interval - This defines the key rotation duration in seconds. When the key period mode is set to Timestamp, this is a mandatory input.
It is recommended to use the Timestamp mode when key rotation is enabled. With our key derivation algorithm’s flooring mechanism, even if the packager sends an irregular start time (e.g., 01:03:10), the algorithm will adjust it to a clean boundary like 01:00:00. This approach allows customers to predict and provision the previous, current, and next Key IDs, enabling seamless playback.
However, when key rotation is enabled in Index mode, the index timestamp is not adjusted by us. As a result, it becomes difficult to predict the correct index without parsing the initial CPIX request.
The algorithm is as follows:
InputParameters = "<TenantId>" + "<Content ID>" + "<Protection Scheme>"+ "<Intended Track Type>" + [<Content Key Period Index>] or [<Content Key Period Start> + <Key Period Interval>]
BaseHash = SHA256(InputParameters)
PartOneHash = Take first 16 bytes of BaseHash
PartTwoHash = Take the last 16 bytes of BaseHash
XorResult = PartOneHash XOR PartTwoHash
Key ID = XorResult to GUID // GUID must be generated using little-endian byte order as in Microsoft .NET.
Sample Key IDs derived according to the algorithm​
For no key rotation:
Parameter | Value |
---|---|
tenantId | 145ac0b6-ad3e-452d-8778-5c02033efea6 |
contentId | test_content |
protectionScheme | cenc |
intendedTrackType | VIDEO |
contentKeyPeriodIndex | null or empty string |
contentKeyPeriodStart | null or empty string |
keyPeriodInterval | null or empty string |
Expected Key ID | 0910abc5-0eb2-ad1d-10de-9e42337059bb |
For index mode key rotation:
Parameter | Value |
---|---|
tenantId | 145ac0b6-ad3e-452d-8778-5c02033efea6 |
contentId | test_content |
protectionScheme | cenc |
intendedTrackType | VIDEO |
contentKeyPeriodIndex | 1743445800 |
contentKeyPeriodStart | null or empty string |
keyPeriodInterval | null or empty string |
Expected Key ID | 18368ea2-7441-e30c-a08d-b6b282731d8a |
For timestamp mode key rotation:
Parameter | Value |
---|---|
tenantId | 145ac0b6-ad3e-452d-8778-5c02033efea6 |
contentId | test_content |
protectionScheme | cenc |
intendedTrackType | VIDEO |
contentKeyPeriodIndex | null or empty string |
contentKeyPeriodStart | 1743445800 |
keyPeriodInterval | 600 |
Expected Key ID | 15084cc0-fb55-0d66-7d5a-e55a9a94b354 |
Sample Key Derivation Algorithm Implementation in JavaScript/NodeJS​
const crypto = require("crypto");
const inputParameters = "145ac0b6-ad3e-452d-8778-5c02033efea6" + "test_content" + "cenc" + "VIDEO" + "1743445800" + "600";
var baseToHashAsByteser = Buffer.from(inputParameters);
// SHA256 hash
var sha256Hasher = crypto.createHash("sha256");
sha256Hasher.update(baseToHashAsByteser);
const baseHashValue = sha256Hasher.digest();
// XOR the first and second half of the base hash to make a 16 bytes hash
const partOne = baseHashValue.slice(0, 16);
const partTwo = baseHashValue.slice(16, 32);
let xorHash = Buffer.alloc(16);
for (let n = 0; n < 16; n++)
xorHash[n] = partOne[n] ^ partTwo[n % partTwo.length];
// Convert to a GUID (based on little-endian bytes)
const kid = createGUID(xorHash);
console.log(kid);
function createGUID(buffer) {
// mind the reverse endianess for the first three parts
return buffer.subarray(0, 4).reverse().toString('hex') + '-' +
buffer.subarray(4, 6).reverse().toString('hex') + '-' +
buffer.subarray(6, 8).reverse().toString('hex') + '-' +
buffer.subarray(8, 10).toString('hex') + '-' +
buffer.subarray(10, 16).toString('hex');
}
The keyID derivation algorithms used by the endpoints /Harmonic
and /Harmonic/v2
are different and will produce different values..