[HLS] Fix decryption issues (#1117)

* Unpad HLS fragments with PKCS#7 according to datatracker.ietf.org/doc/html/rfc8216
* media_sequence should only be incremented in for media fragments
* The native decryption should only be used if ffmpeg is unavailable since it is significantly slower. Closes #1086

Authored by: shirt-dev, pukkandan
This commit is contained in:
shirt 2021-09-28 14:53:24 -04:00 committed by GitHub
parent 80c360d7aa
commit 7687c8ac6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 15 additions and 8 deletions

View file

@ -355,7 +355,8 @@ def decrypt_fragment(fragment, frag_content):
# not what it decrypts to. # not what it decrypts to.
if self.params.get('test', False): if self.params.get('test', False):
return frag_content return frag_content
return aes_cbc_decrypt_bytes(frag_content, decrypt_info['KEY'], iv) decrypted_data = aes_cbc_decrypt_bytes(frag_content, decrypt_info['KEY'], iv)
return decrypted_data[:-decrypted_data[-1]]
return decrypt_fragment return decrypt_fragment

View file

@ -9,6 +9,7 @@
from .external import FFmpegFD from .external import FFmpegFD
from ..compat import ( from ..compat import (
compat_pycrypto_AES,
compat_urlparse, compat_urlparse,
) )
from ..utils import ( from ..utils import (
@ -68,14 +69,20 @@ def real_download(self, filename, info_dict):
man_url = urlh.geturl() man_url = urlh.geturl()
s = urlh.read().decode('utf-8', 'ignore') s = urlh.read().decode('utf-8', 'ignore')
if not self.can_download(s, info_dict, self.params.get('allow_unplayable_formats')): can_download, message = self.can_download(s, info_dict, self.params.get('allow_unplayable_formats')), None
if info_dict.get('extra_param_to_segment_url') or info_dict.get('_decryption_key_url'): if can_download and not compat_pycrypto_AES and '#EXT-X-KEY:METHOD=AES-128' in s:
self.report_error('pycryptodome not found. Please install') if FFmpegFD.available():
return False can_download, message = False, 'The stream has AES-128 encryption and pycryptodome is not available'
else:
message = ('The stream has AES-128 encryption and neither ffmpeg nor pycryptodome are available; '
'Decryption will be performed natively, but will be extremely slow')
if not can_download:
message = message or 'Unsupported features have been detected'
fd = FFmpegFD(self.ydl, self.params) fd = FFmpegFD(self.ydl, self.params)
self.report_warning( self.report_warning(f'{message}; extraction will be delegated to {fd.get_basename()}')
'%s detected unsupported features; extraction will be delegated to %s' % (self.FD_NAME, fd.get_basename()))
return fd.real_download(filename, info_dict) return fd.real_download(filename, info_dict)
elif message:
self.report_warning(message)
is_webvtt = info_dict['ext'] == 'vtt' is_webvtt = info_dict['ext'] == 'vtt'
if is_webvtt: if is_webvtt:
@ -232,7 +239,6 @@ def is_ad_fragment_end(s):
elif line.startswith('#EXT-X-DISCONTINUITY'): elif line.startswith('#EXT-X-DISCONTINUITY'):
discontinuity_count += 1 discontinuity_count += 1
i += 1 i += 1
media_sequence += 1
# We only download the first fragment during the test # We only download the first fragment during the test
if self.params.get('test', False): if self.params.get('test', False):