Customer Sign In

upLynk

Digital rights management (DRM)

Overview

This guide covers topics related to controlling how and when your content is consumed by your users; this functionality it sometimes referred to as "Digital Rights Management" or "DRM".

Before users can begin playing media on a website or in an application, often various checks are performed to restrict access to the media. For example, some sites may require the user to be logged in, or to live in a particular geographic region, or to have previously purchased access to a movie.

In general, the earlier these checks are performed, the better the resulting user experience. For example, a pay-per-view movie site may show its detailed catalog only to logged in users, and it won't initiate playback of a movie unless the user has already purchased access to it. Instead of letting the user try to play a movie and getting an error message, the site would prevent playback from being started at all and would guide the user towards the purchasing page.

Another example could be a website that displays ad-supported content but has rights to play the content only to people in Europe. A poorly designed website would let a non-European visitor navigate the catalog, find a movie to watch, and click a play button - only to then present them with an error message. A better design would warn the user up front that content isn't playable, perhaps filtering from the catalog unplayable content so that it is never even presented to the user at all.

The upLynk system uses strong encryption to prevent against unauthorized access to your media assets. As with your website or application, the upLynk system must also determine if a given user should be allowed to play an asset from your media library. Two main strategies exist for accomplishing this:

  • (a) upLynk could provide tools that let you express in the upLynk system the same checks or business rules your website uses (user must be logged in, user must be in Europe, etc.)
  • (b) your website could tell upLynk if playback should be allowed

The first approach is common but inherently problematic because your business rules or playback authorization checks must be implemented in two separate systems, which is more work for you but also a problem because the two systems may have subtle differences in how they operate. Further, you must always ensure that the two systems are kept in sync - a business rule change in one system must be exactly replicated in the other system, and the changes must be implemented in tandem. Additionally, any underlying data the two systems use must always be kept in sync. For example, the databases that determine where users are physically located change often and vary by vendor. If you need to restrict access by geographic location, you'd also need to ensure that your database is exactly the same version as upLynk's, otherwise your website may determine playback should be allowed but the upLynk system might block access, leading to a very bad user experience.

The second strategy for determining if playback should be allowed is relatively simple. The core idea is that your website or application is already deciding whether or not playback should be allowed, so all you really need is some secure, reliable way to communicate that decision to upLynk. The disadvantage of this approach is that it requires a little extra one-time integration work when you first set up your website. The advantage, however, is that you avoid all of the problems with the other strategy: you never have to worry about maintaining two copies of your business rules in two separate systems. upLynk has chosen this second strategy because it is simpler, more robust, and encourages the best user experience.

Playback Permission

You grant a user permission to play an asset by preparing a small message known as an authorization token (sometimes also referred to as an "auth token" or a "playback token"). This message is digitally signed (to ensure that only you can grant permission to play content) and added to the end of the playback url. From there, playback proceeds normally - the player is told to play the URL.

The upLynk system inspects the authorization token on the playback URL and performs various checks to ensure it hasn't expired or been tampered with and that it was created for the user trying to play the content. If all checks pass, the user is allowed to play the content.

The authorization token is your way of communicating to upLynk your permission for the user to play some content, and it works with both individual assets and live channels.

An authorization token is required to initiate any form of playback. There are a few scenarios, however, in which upLynk hosts a playback page and generates authorization tokens for you. The first is with test player URLs, which are links to a standalone player for an asset or live channel. These exist to make it easy to demo content or to aid in testing. Each asset and live channel has a unique test player URL. Anyone who has the URL can play the content until you have the upLynk system generate a new test player URL, at which time the old URL for that asset or channel will expire.

In addition to a test player URL, each asset or channel has an embed URL. These exist to make it easy to embed content on a web page such as a blog. Playback access can be restricted so that the video can be embedded only on certain domain names. As with test player URLs, embed URLs can be expired and regenerated.

Both test player and embed URLs exist to make it easy to get started, but in general it is preferable to use authorization tokens to control whether or not content can be played.

Creating authorization tokens

This section describes the actual steps required to create an authorization token. A lot of detail is provided here, but don't be overwhelmed by it - in most programming languages it requires only a few lines of code.

Auth tokens are specially constructed query strings appended to the end of a playback URL. At a high level, they consist of:

  • Core identification parameters
  • Zero or more customization parameters
  • sig - The digital signature, the final query string parameter

Core identification parameters

The first few query string parameters convey the main information about the auth token. In general, these parameters are required in all auth tokens.

tcToken check algorithm version (currently use a value of '1')
expExpiration: Time in UTC when this token expires
rnRandom number: random integer to increase uniqueness of signature
ctContent type: 'a' for asset, 'c' for linear channel, or 'e' for an event.
cidContent ID: asset, channel, or event ID (get this from the upLynk CMS)

The expiration for the token should be kept as low as possible, but remember that different systems might have slightly different clock values, so making it expire in less than 5-10 seconds is probably too low and will prevent some legitimate users from playing the content.

As an example, the following inputs will be used to generate the core identification parameters:

current timeJanuary 16, 2013 13:10:03 UTC
desired lifespan60 seconds
asset IDea10fa402fec4bbe996019a0827e6c38

The timestamp is measured in standard Unix time (i.e. seconds since the epoch), and the above date maps to 1358341803, so an expiration time 60 seconds after that would be 1358341863.

Each auth token includes a random integer to help ensure uniqueness. For this example, a value of 4114845747 will be used.

Using the above, the final value for the first part of the auth token would be:

tc=1&exp=1358341863&rn=4114845747&ct=a&cid=ea10fa402fec4bbe996019a0827e6c38

In some cases, it may be more convenient to identify the content by its external ID. In this case, you can omit the cid parameter and instead supply the external ID in the eid parameter, and add an additional oid parameter with your owner (account) ID. For example, if your account ID is a735c65ea4041685bc74c0a375326cc5 and you have an asset with an external ID of mtg_003:

tc=1&exp=1358341863&rn=4114845747&ct=a&eid=mtg_003&oid=a735c65ea4041685bc74c0a375326cc5

The oid parameter is required when using eid.

The oid parameter is also used with content sharing, in which you are generating an auth token for another user's content that has been shared with you in the CMS. In this scenario, the oid parameter is required and its value should be your owner (account) ID. The system will verify that the content you are attempting to play resides in one or more libraries that have been shared with you. If the URL format uses the content owner ID (e.g. a URL that uses the content owner ID and an external ID), the actual content owner ID should still be used - only the oid is modified to use your owner ID.

For example, if an owner with owner ID aaaaaaaa has an asset with an external ID of my_asset, and has shared this asset with another owner whose ID is bbbbbbbb, then that owner could generate an auth token and playback URL of the form:

http://content.uplynk.com/ext/aaaaaaaa/my_asset.m3u8?cid=...&oid=bbbbbbbb&... 

Customization parameters

After the core identification parameters you can add optional customization parameters that affect how the content will be played. These parameters are normal HTTP query string parameters in the sense that you should take care to properly escape any special characters if needed.

ratesRestricts playback to the given range of bitrates, from low to high, in Kbps. By default, all bitrates are allowed; note that restricting bitrates can adversely impact adaptive streaming capabilities.
Example:  rates=0-1024 // Caps playback to 1Mbps
Example:  rates=600- // Allows bitrates of 600kbps and up
delaySets the playback delay behind live. By default, live channels trail the live horizon by 60-75 seconds. This parameter can be used to introduce a larger artificial delay, e.g. to adjust for timezones or other reasons. The value specifies the delay in seconds. The system default value is -1, which means to use the channel's standard delay.
Example:  delay=7200 // Shift playback two hours backwards
tsArtificially sets the playback start time to the given timestamp (seconds since the epoch in UTC). Cannot be used any farther back in time than the schedule and assets for the linear stream exist.
Example:  ts=1368529129 // Start playback at 2013-05-14 10:58:49 UTC
euidExternal user ID - an optional string to identify the current user, such as a user ID in your own system. If provided, this value is passed to ad decision systems as the user identifier and is also included in many logging messages. The string must be no more than 100 characters in length and consist entirely of alphanumeric characters, underscores, or dashes. The value is not interpreted by the upLynk system in any way, and is merely passed along unmodified.
Example:  euid=145XnM_0bHt2hZIGw8twtl3ccpjVF5rRVj6VJ_ZgqvtY2KmH
adAd server definition name. Using the CMS UI you can define ad server configurations so that the upLynk system can call the ad server of your choice to obtain ads for use during playback. This parameter identifies which ad server definition to use; omitting it allows playback to proceed without any ads.
Example:  ad=fw2
ad.kvAd server key-value pairs parameter. Use this parameter to pass key-value pairs to the ad server. Values are passed in as a comma separated string alternating keys and values.
Example:  ad.kv=key1,value1,key2,value2
ad.*Ad server-specific parameters. The 'ad.' prefix is removed from the name and the rest is passed to the ad server when acquiring ads for playback. These parameters are ignored if the ad parameter is not also included somewhere in the auth token.
Example:  ad.account=vz1234&ad.ctxid=MA_99_174
akThe name of the application key that is required to be present to use this auth token. For legacy implementations, the value is just the key name. Newer implementations should use the form 1.<appKeyName>.
Example:  ak=mykey // legacy format
Example:  ak=1.mykey // current format
startSets the playback start offset in units of seconds. The offset will be rounded down to the nearest slice boundary. Content prior to this offset will not be not be included in playback. Does not work with channel URLs.
Example:  start=95.3 // Start playback at the slice containing second 95.3
stopSets the playback stop time in units of seconds. The offset will be rounded up to the nearest slice boundary. Content after this offset will not be not be included in playback. Does not work with channel URLs.
Example:  stop=110.9 // Stop playback after the slice containing second 110.9
sstartSets the playback start point in slice numbers (0-based). Content prior to the starting slice will not be not be included in playback. Does not work with channel URLs.
Example:  sstart=15 // Start playback at slice 15.
sstopSets the playback stop point in slice numbers (0-based). The slice specified will be the final slice included in playback, i.e. playback will stop after this slice plays. Does not work with channel URLs.
Example:  sstop=20 // Stop playback after slice number 20
raysRestricts playback to the given rays (bitrates), where each ray is identified by its letter ('a' is the lowest quality ray). By default, all rays are allowed, although the 'a' ray is usually excluded on non-iOS platforms as this ray is extremely low quality and exists only because the Apple AppStore requires it. Characters for which there is no corresponding ray are ignored. Note that the order of rays listed is significant in that acts as a hint to the client as to which ray it should try first when starting playback. Thus, listing a lower ray first will force playback to start at a low quality, while listing a higher ray first will tell a client to attempt to start at a higher ray (although the client may still opt to use a lower ray if network conditions are poor). The upLynk system by default suggests that clients play the 'd' or 'e' ray. Care should be taken when using this parameter as it interferes with normal adaptive streaming behavior and can therefore create poor user experiences.
Example:  rays=dcba // prevents access to higher rays
is_adManually forces an asset to be reported as an ad in the push logs asset play start event. This is not needed if the system is inserting ads automatically, but might be useful in cases where ads are being managed and inserted by an external system, and you still want the upLynk push logs to reflect that the asset was played as an ad.
Example:  is_ad=1 // Have playback reported as an ad in the push logs
repl The value associated with this parameter defines the replacement plugin to use for playback.
expand
Comma-separated list of parameters to expand. The values that will result are defined in the CMS under the account advanced settings' "Parameter Expansion" tab. The values defined there should be a string in URL parameter format. Parameter expansion can be used to shorten URL lengths, make it easier to adjust parameters across multiple channels, or hide actual parameters from viewers.
For example, if my usual playback URL consisted of the following parameters:
rays=efgabcd&delay=7200&ad=myads&ad.access_level=1&ad.flex=0&ad.adUnit=midroll&...
I could define the following expansion parameters in the advanced settings of my account in the CMS:
Expansion ParameterValue
playback_01rays=efgabcd&delay=7200
ad_01ad=myads&ad.access_level=1&ad.flex=0&ad.adUnit=midroll
I would then use the following expand parameter to achieve the same functionality:
expand=ad_01,playback_01&...
Note that the expand parameter name and values should be used in the signature generation and not the expected expanded parameters.
The following parameters are not supported through parameter expansion:
  • linearv
  • jsonp
  • pbs
  • skip_drm
  • oid
  • ‘start’ and ‘stop’ for channel schedules
  • stageplayer
ptid

Tracks the playback of linear and/or on-demand content by tagging it with an organizational group or a category (e.g., MyChannel). This case-sensitive value may consist of up to 32 alphanumeric characters, dashes, and underscores.

Note: Although tracking data is logged, it is currently inaccessible. However, it will eventually be exposed via log data.

Note: If this parameter has not been defined in the playback URL, then content playback will be tracked under the "unknown" category.

Customization parameters are added after the core identification parameters in the query string. For example:

tc=1&exp=1358341863&rn=4114845747&ct=a&cid=ea...&rays=dcba&pk=myapp&...

For testing purposes, auth token parameters can also be added as query string parameters to asset and channel test player URLs from the CMS.

Digital Signature

The digital signature ensures that the auth token originated from you and has not been tampered with. It must always be the final parameter of the query string, and it is computed by taking the entire query string so far, computing the HMAC/SHA-256 of that value using your secret API key (which can be obtained in the CMS), and then appending it to the query string as a parameter named sig.

As an example, the following inputs will be used to generate the digital signature as well as the final auth token query string. To make the example more consise, a shorter than normal query string is used.

query string so fartc=1&exp=1358341863&rn=4114845747&ct=a&cid=ea10fa402fec4bbe996019a0827e6c38
secret API key (from the CMS)WxQpQhHFmE4hTWA4TGLu6rYeNuKgYrWwlCLmSKRb
customization parametersallow rays A, B and C only

Using the secret API key, the HMAC/SHA-256 for the existing query string is computed and found to be:

37ecd4cbcad4bc156daac10a2bf9ccf38fb7a8d83fa25f257a62474a86b82cbf

This value is then appended onto the query string with &sig=:

tc=1&exp=1358341863&rn=4114845747&ct=a&cid=ea10fa402fec4bbe996019a0827e6c38&sig=37ecd4cbcad4bc156daac10a2bf9ccf38fb7a8d83fa25f257a62474a86b82cbf

The digital signature is always the final query string parameter. The above value constitutes the entire authorization token. This can be appended as the query string on a playback URL. For example, given a full playback URL would look something like:

http://content.uplynk.com/ea10fa402fec4bbe996019a0827e6c38.m3u8?tc=1&exp=1358341863&rn=4114845747&ct=a&cid=ea10fa402fec4bbe996019a0827e6c38&sig=37ecd4cbcad4bc156daac10a2bf9ccf38fb7a8d83fa25f257a62474a86b82cbf

Auth tokens are always generated server side - you should never generate auth tokens from inside an application you give to your users, for example. Remember, anyone who has access to your secret API key can potentially play any of your content without any restrictions.

Sample code

Below is some sample code for the above walkthrough.

Authorization token generation example (Python)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import hashlib, time, hmac, urllib, random

# inputs
apiKey = 'WxQpQhHFmE4hTWA4TGLu6rYeNuKgYrWwlCLmSKRb' # from the CMS UI

# combine all of the parameters except the signature
queryStr = urllib.urlencode(dict(
    tc = '1', # token check algorithm version
    exp = int(time.time()) + 60, # expire 60 seconds from now
    rn = str(random.randint(0, 2**32)), # random number
    ct = 'a', # an asset
    cid = 'ea10fa402fec4bbe996019a0827e6c38', # the asset's ID
    rays = 'dcba', # customization parameter
))

# compute the signature and add it to the *end*
sig = hmac.new(apiKey, queryStr, hashlib.sha256).hexdigest()
queryStr = queryStr + '&sig=' + sig

# The token would then be added to a playback URL, e.g.
url = 'http://content.uplynk.com/ea10fa402fec4bbe996019a0827e6c38.m3u8'
url = url + '?' + queryStr
Authorization token generation example (PHP)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<?php
// inputs
$apiKey = "WxQpQhHFmE4hTWA4TGLu6rYeNuKgYrWwlCLmSKRb" // from the CMS UI

// combine all of the parameters
$msg = array();
$msg["tc"] = "1"; // token check algorithm version
$msg["exp"] = time() + 60; // expire 60 seconds from now
$msg["rn"] = rand(); // random number
$msg["ct"] = "a"; // an asset
$msg["cid"] = "ea10fa402fec4bbe996019a0827e6c38"; // the asset's ID
$msg["rays"] = "dcba"; //customization parameter

// Calculate signature
$msg["sig"] = hash_hmac("sha256", http_build_query($msg), $apiKey);

// The token query string would then be added to a playback URL, e.g.
$url = 'http://content.uplynk.com/ea10fa402fec4bbe996019a0827e6c38.m3u8'
return $url . '?' . http_build_query($msg);

Encrypting Query Strings (optional)

Encrypting your playback query string can be useful if you find that ad blockers are preventing your playback URLs from being requested (this can sometimes happen if an ad blocker is filtering URLs that contain strings like 'ad'). This process is entirely optional.

The process for encrypting your query strings is as follows:

  1. Create your query string (including signature) as documented above.
  2. Choose one of your enabled API keys
  3. Generate the MD5 hash of your chosen API key. This hash should be exactly 16 bytes (128 bits) long.
  4. Encrypt the query string using AES-128 in CBC mode
    1. Use the MD5 hash as the key for this operation
    2. Use 16 null bytes as the initialization vector
  5. Base64 encode the encryption result in a URL-safe way. Generally, making a Base64 encoding result URL-safe means replacing all + characters with - and all / characters with _. Some languages & libraries have URL-safe-specific functions for doing this.
  6. Create an entirely new query string with exactly two parameters, and use this new query string in your playback URL.
    1. cqs=<URL-safe base64-encoded version of encrypted query string> (cqs: crypted query string)
    2. kid=<the ID corresponding to the API key used to perform the encryption> (kid: key ID). This value may be found in the CMS > Settings (gears tab) > Playback tokens, next to the corresponding to your API key.

Example Python code for the above procedure:

Encrypting Query Strings (Python)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import base64
import hashlib

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.padding import PKCS7
from cryptography.hazmat.backends import default_backend

API_KEY = 'cL8Z0+DHCJZqpsN6/tlB01oyxFfeElj3t7PnwWRI'
KEY_ID = 'ad5ba943177f4a1587795a9ee8d47293'

MD5_KEY = hashlib.md5(API_KEY).digest()

# This query string is signed for a hypothetical asset with ID 340ca73eb07c4f4ca08b804c47a91f1b,
# owned by user with ID ba8cb548202840d48d1255885d7bb2f3
SIGNED_QUERY_STRING = 'ad=fwvod&cid=340ca73eb07c4f4ca08b804c47a91f1b&oid=ba8cb548202840d48d1255885d7bb2f3&exp=1492596978713&test=1&rn=310292100&tc=1&ct=a&sig=2ff94739b021912712adafeccd6fa291f11eef0648c3b18b30224b84e0590b4f'

def encrypt_aes_128_cbc(key, data):
    backend = default_backend()
    aes = algorithms.AES(key)
    iv = b'\x00' * 16  # Initialization vector of 16 null bytes
    cbc = modes.CBC(iv)
    padder = PKCS7(aes.block_size).padder()
    padded_data = padder.update(data) + padder.finalize()

    encryptor = Cipher(aes, cbc, backend).encryptor()
    result = encryptor.update(padded_data) + encryptor.finalize()
    return result

# Encrypt the query string using the MD5 hash of the selected API key
crypted_qs = encrypt_aes_128_cbc(MD5_KEY, SIGNED_QUERY_STRING)

# Encode the encryption result as URL-safe base64
encoded_qs = base64.urlsafe_b64encode(crypted_qs)

new_params = {'cqs': encoded_qs, 'kid': KEY_ID}

# This will be the new playback URL to request from Uplynk.
url_with_crypted_qs = 'https://content.uplynk.com/340ca73eb07c4f4ca08b804c47a91f1b.m3u8?cqs={cqs}&kid={kid}'.format(**new_params)

print url_with_crypted_qs

### Value of `url_with_crypted_qs` is:
### https://content.uplynk.com/340ca73eb07c4f4ca08b804c47a91f1b.m3u8?cqs=gYXTAVtWRvk0qCs8pM9CmgprLvyQt9jNDETBL4ApLCqf2iFh-c9tXSk2Q_EbAAFc4q19KTikvqx8-StlruVaLafXU2NciESn-ZNPa-thp8UXSWwKszIp8oBjx8SJr9fcwUmu9El-w2q9lQ61nu1pk1JxomEraZAtfie9k8f5vAklpyYg5Ejd6i7iokxFO1XflOJFkhnDHp1ozCXVgh-rYKuCbbOEUwAaGYgd4zjn88GBgO1ZY8Jn3OFyGssvOydsPAnRjQmPsfFE24wYsp1Mlg==&kid=ad5ba943177f4a1587795a9ee8d47293

Uplynk inspects incoming playback URLs for the two parameters relating to query string encryption. If it finds them both, it will look for an enabled API key from the content owner corresponding with the kid value, and use that key to decrypt the cqs value. Once the system has decrypted the query string it will continue as normal, establishing a playback session using the information from the decrypted string.

Application keys

When content keys are transferred from the upLynk system to a user's machine for playback, they are protected by transferring them over a secure HTTPS connection. In certain environments it is possible to transfer the keys using an additional layer of encryption using application keys (or "app keys"). An application key is a secret key embedded in a device or application; when requests are made for the content decryption keys, they are still transferred to the client over HTTPS but are further encrypted using the secret key embedded in the application. Because app keys rely on client side functionality to be present, they work only with applications and not web-based playback, and the application must make use of an upLynk client SDK.

App keys are enabled by including the ak configuration parameter when generating an auth token. If this parameter is present, the token will work only with an app key and cannot be used in other environments. The parameter value is a name you assigned the app key in the upLynk CMS (on the Gears tab, Playback Tokens subtab). Your client application then provides the same key to the upLynk client SDK, and all key delivery automatically makes use of this additional layer of security. In addition to using app keys, you should take steps to protect your application against reverse engineering.

Note that app keys are displayed in the CMS UI as a hex string for convenience, but they keys themselves should be treated as binary data. For example, if the CMS UI shows a key like 88051e9b, the actual key is that sequence of bytes: 0x88 0x05 0x1E 0x9B and not the ASCII characters 8, 8, 0, 5, etc.