streamlink: Incorrect AES key length (48 bytes)

Hello all,

I have the following problem when i am trying to download an encrypted m3u8 link(AES 128) :

[cli][info] Found matching plugin hls for URL https://cdn.spayee.in/spees/w/o/5b49b8f2e4b02ac9d5e71945/v/5bcd586ee4b04b639bfddcf2/u/5c28ab6fe4b0efa6ebda1830/t/857c9994539db0b48bd818abaa6bcaaa/p/assets/videos/2018/10/22/5bcd586ee4b04b639bfddcf2/hls_350k_.m3u8
[cli][info] Available streams: live (worst, best)
[cli][info] Opening stream: live (hls)
Exception in thread Thread-HLSStreamWriter:
Traceback (most recent call last):
  File "threading.py", line 916, in _bootstrap_inner
  File "D:\ww\streamlink1\packages\streamlink\stream\segmented.py", line 167, in run
    self.write(segment, result)
  File "D:\ww\streamlink1\packages\streamlink\stream\hls.py", line 124, in write
    sequence.num)
  File "D:\ww\streamlink1\packages\streamlink\stream\hls.py", line 80, in create_decryptor
    return AES.new(self.key_data, AES.MODE_CBC, iv)
  File "D:\ww\streamlink1\packages\Crypto\Cipher\AES.py", line 260, in new
    return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
  File "D:\ww\streamlink1\packages\Crypto\Cipher\__init__.py", line 130, in _create_cipher
    return modes[mode](factory, **kwargs)
  File "D:\ww\streamlink1\packages\Crypto\Cipher\_mode_cbc.py", line 232, in _create_cbc_cipher
    cipher_state = factory._create_base_cipher(kwargs)
  File "D:\ww\streamlink1\packages\Crypto\Cipher\AES.py", line 130, in _create_base_cipher
    raise ValueError("Incorrect AES key length (%d bytes)" % len(key))
ValueError: Incorrect AES key length (48 bytes)

I hope somebody from here can help me.

Thanks!

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 18 (6 by maintainers)

Most upvoted comments

the decrypt_key method is changed. (Note: pseudocode)

    def _decrypt_key(self,data):
        tmp1 = data[0:16]
        tmp2 = data[32:48]
        dec1 = AES.new(base64.b64decode(spka),AES.MODE_ECB)
        tmp3 = dec1.decrypt(tmp1)
        dec2 = AES.new(tmp3,AES.MODE_ECB)
        return dec2.decrypt(tmp2)

And the spka key is from web page’s javascript and may be dynamically generated.

Sample

<script type="text/javascript" id="dsf239df">
	var ctx = "/s",
		isFirstLogin = "", cjs = CryptoJS;
		
	var defaultCoreColor = "#5755d9", theme1CoreColor = "#572b7a", theme2CoreColor = "#3f51b5", theme3CoreColor = "#009051", 
		theme4CoreColor = "#596439", theme5CoreColor = "#F0591D", theme6CoreColor = "#28a745", theme7CoreColor = "#1bbc9b", 
		theme8CoreColor = "#f38f20", theme9CoreColor = "#c0af01", theme10CoreColor = "#e8d3a4", spka = "YjM3ZGVhNWU4N2I1MTI2Yw==",
		webColor = defaultCoreColor,

spka = “YjM3ZGVhNWU4N2I1MTI2Yw==”

You can make a option like “–spayee-spka=xxx” to pass spka key to the this plugin.

A simple plugin implemention

import re
import logging

from Crypto.Cipher import AES
from streamlink import StreamError
from streamlink.plugin import Plugin
from streamlink.stream import HLSStream
from streamlink.stream.hls import HLSStreamWriter, HLSStreamReader

log = logging.getLogger(__name__)

class SpayeeStreamWriter(HLSStreamWriter):
    def _decrypt_key(self,data):
        tmp1 = data[0:16]
        tmp2 = data[32:48]
        dec1 = AES.new(tmp2,AES.MODE_ECB)
        tmp3 = dec1.decrypt(tmp1)
        dec2 = AES.new(tmp3,AES.MODE_ECB)
        return dec2.decrypt(tmp2)

        
    def create_decryptor(self, key, sequence):
        if key.method != "AES-128":
            raise StreamError("Unable to decrypt cipher {0}", key.method)

        if not self.key_uri_override and not key.uri:
            raise StreamError("Missing URI to decryption key")

        key_uri = self.key_uri_override if self.key_uri_override else key.uri

        if self.key_uri != key_uri:
            res = self.session.http.get(key_uri, exception=StreamError,
                                        retries=self.retries,
                                        **self.reader.request_params)
            res.encoding = "binary/octet-stream"
            self.key_data = self._decrypt_key(res.content)
            self.key_uri = key_uri

        iv = key.iv or num_to_iv(sequence)

        # Pad IV if needed
        iv = b"\x00" * (16 - len(iv)) + iv

        return AES.new(self.key_data, AES.MODE_CBC, iv)


class SpayeeStreamReader(HLSStreamReader):
    __writer__ = SpayeeStreamWriter

class SpayeeStream(HLSStream):
    def open(self):
        reader = SpayeeStreamReader(self)
        reader.open()

        return reader


class Spayee(Plugin):
    url_re = re.compile(r"""https://cdn.spayee.in/.*.m3u8""")


    def __init__(self, url):
        super(Spayee, self).__init__(url)

    @classmethod
    def can_handle_url(cls, url):
        return cls.url_re.match(url) is not None

    def _get_streams(self):
        return {"live":SpayeeStream(self.session,self.url)}



__plugin__ = Spayee

save it to streamlink\plugins\spayee.py

Only tested under python 2.7