Search code examples
c#aeshttp-live-streamingm3u8

Decrypt M3U8 Playlist encrypted with AES-128 without IV


I'm currently building an Application for downloading M3U8 Playlists, but i've run into an issue: If the Playlist is encrypted with AES-128, e.g. has a line like this:

#EXT-X-KEY:METHOD=AES-128,URI="https://website.com/link.key",IV=0xblablabla

I have to decrypt the segments before writing them to the output file, and if an IV is present the below code does work for me, but if the IV property doesn't exist the decryption produces a wrong result:

var iv = "parsed iv"; // empty if not present
var key_url = "parsed keyurl";

var AES = new AesManaged()
{
    Mode = CipherMode.CBC,
    Key = await Client.GetByteArrayAsync(key_url)
};

if (!string.IsNullOrEmpty(iv))
    AES.IV = Functions.HexToByte(iv.StartsWith("0x") ? iv.Remove(0, 2) : iv);
else
    AES.IV = new byte[16];

//...

using (FileStream fs = new FileStream("file.ts", FileMode.Create, FileAccess.Write, FileShare.Read))
{
    var data = DownloadSegment(...); // Downloads segment as byte array (encrypted)

    byte[] temp = new byte[data.Length];

    ICryptoTransform transform = AES.CreateDecryptor();
    using (MemoryStream memoryStream = new MemoryStream(data))
    {
        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read))
        {
            cryptoStream.Read(temp, 0, data.Length);
        }
    }

    await fs.WriteAsync(temp, 0, temp.Length);
}

(This is obviously just a code snippet, containing the decryption part, since all the parsing and downloading does work fine).

Does anyone of you know how to decrypt an AES-128 encrypted segment in a M3U8 Playlist file if there is no IV present, e.g. just

#EXT-X-KEY:METHOD=AES-128,URI="https://website.com/link.key"?

Any help is greatly appreciated. Thanks in advance!


Solution

  • The HLS spec states [1]:

    An encryption method of AES-128 signals that Media Segments are completely encrypted using the Advanced Encryption Standard (AES) [AES_128] with a 128-bit key, Cipher Block Chaining (CBC), and Public-Key Cryptography Standards #7 (PKCS7) padding [RFC5652]. CBC is restarted on each segment boundary, using either the Initialization Vector (IV) attribute value or the Media Sequence Number as the IV; see Section 5.2.

    So you have to use the value of the EXT-X-MEDIA-SEQUENCE tag in the variant playlist. Be sure to extrapolate, i.e. increment it for each segment.

    [1] https://www.rfc-editor.org/rfc/rfc8216#section-4.3.2.4