Annex F of Adobe’s FLV specification (version 10.1) describes how Flash Media Rights
Management Server and Flash Access protect FLV files. The file stays an ordinary FLV:
the encryption metadata is a script data tag near the start, and each protected tag sets
the Filter bit of its tag
header and carries a small header of its own before the encrypted body. Most
metadata stays in the clear (audio or video, key frame, codec), so servers and players
can handle the file without decrypting it. Players that do not support encryption skip
tags with the Filter bit set, as if they were a new tag type.
How a file is encrypted
- An encryption header, holding what is needed to decrypt the file (the algorithm, the key length and how to get the key), is stored as script data straight after the FLV header, before any encrypted content.
- Tags that carry content are encrypted. To save work, only some of them may be, such as key frames.
- An encrypted tag has its Filter bit set. The filter to apply is named in the tag.
- The
onMetaDatascript data is always left in the clear. Script data that is not encrypted must not have the Filter bit set, so a player can find it.
With version 2 of the encryption header (Flash Access 2.0), every audio and video tag should have the Filter bit set, whether or not it is encrypted; the selective encryption filter then says whether this tag is.
AdditionalHeader object
Encrypted files carry a script data tag named |AdditionalHeader (with the
vertical bar), at time 0, straight after the onMetaData tag. Its value is
an object with one property.
| Property | Type | Meaning |
|---|---|---|
Encryption | Encryption Header object | The encryption header, below. |
Encryption Header object
| Property | Type | Meaning |
|---|---|---|
Version | Number | 1 for Flash Media Rights Management Server 1.x, 2 for Flash Access 2.0. Files of both versions exist, and readers are expected to handle both. |
Method | String | Standard. |
Flags | Number | 0. |
Params | Standard Encoding Parameters object | The parameters of the Standard method, below. |
SigFormat | String | Version 1 only. Adobe gives no details. |
Signature | Long string | Version 1 only. Adobe gives no details. |
Standard Encoding Parameters object
| Property | Type | Meaning |
|---|---|---|
Version | Number | 1. |
EncryptionAlgorithm | String | AES-CBC: AES in cipher block chaining mode, padded as RFC 2630 says. |
EncryptionParams | AES-CBC Encryption Parameters object | One property, KeyLength (Number): the key length in bytes, 16 (128 bits). |
KeyInfo | Key Information object | How to get the decryption key, below. |
Key Information object
How the DRM client gets the key is outside the specification; these objects only point to it.
| Property | Type | Meaning |
|---|---|---|
SubType | String | APS (Adobe Policy Server, an online key agreement protocol) with version 1; FlashAccessv2 (an online key retrieval protocol) with version 2. |
Data | Object | For APS, an Adobe Policy Server object, which Adobe does not describe because conforming applications no longer write it. For FlashAccessv2, a FlashAccessv2 object. |
| FlashAccessv2 property | Type | Meaning |
|---|---|---|
Metadata | Long string | Base64-encoded metadata the DRM client uses to fetch the key. Its contents are not published. |
EncryptionTagHeader
When a tag’s Filter bit is 1, its data starts (after the audio or video tag header, if any) with this header and the filter’s parameters.
| Field | Type | Meaning |
|---|---|---|
| NumFilters | UI8 | The number of filters applied to the packet: 1. |
| FilterName | STRING | Encryption with encryption header version 1; SE (selective encryption) with version 2. A STRING, as in SWF files, is text ended by a zero byte. |
| Length | UI24 | The length of FilterParams in bytes. |
| FilterParams | see below | The filter’s parameters. |
FilterParams
| Filter | Field | Type | Meaning |
|---|---|---|---|
Encryption (EncryptionFilterParams) | IV | UI8[16] | The initialisation vector for AES-CBC. Every packet with this filter is encrypted. |
SE (SelectiveEncryptionFilterParams) | EncryptedAU | UB[1] | 1 if this packet is encrypted, 0 if it is not. |
| Reserved | UB[7] | 0. | |
| IV | UI8[16] | Only when EncryptedAU is 1: the initialisation vector for AES-CBC. |
Encrypted body
An encrypted tag’s body (its AudioTagBody, VideoTagBody or ScriptTagBody) is replaced by the cipher text, then the encrypted padding. A tag that is not encrypted holds the plain body.
| Field | Type | Meaning |
|---|---|---|
| Content | UI8[plain text length] | The cipher text. |
| Padding | UI8[padding length] | The encrypted padding. |
The padding follows RFC 2630: the body is padded to the next multiple of the cipher’s
block size k (16 bytes for AES-128) with k − (length mod k) bytes, each holding that
number. Padding is always added, so a 32-byte body becomes 48 bytes, the last 16 of
them 0x10.
Worked example
The start of an encrypted video tag’s data with version 2 (selective encryption),
after the 11-byte tag header, whose first byte is 0x29 (Filter 1, type
9):
| Bytes | Meaning |
|---|---|
17 | VideoTagHeader: key frame, AVC |
01 00 00 00 | AVCPacketType 1, CompositionTime 0 |
01 | NumFilters 1 |
53 45 00 | FilterName SE and its zero byte |
00 00 11 | Length 17 |
80 | EncryptedAU 1 |
| 16 bytes | IV |
| the rest | The encrypted NAL units and padding |