From 3426fc145f7abc9cc526a196d1701a93bf551b2e Mon Sep 17 00:00:00 2001 From: rlaphoenix <17136956+rlaphoenix@users.noreply.github.com> Date: Fri, 17 May 2024 01:15:37 +0100 Subject: [PATCH] fix(HLS): Decrypt AES-encrypted segments separately We cannot merge all the encrypted AES-128-CBC (ClearKey) segments and then decrypt them in one go because each segment should be padded to a 16-byte boundary in CBC mode. Since it uses PKCS#5 or #7 style (cant remember which) then the merged file has a 15 in 16 chance to fail the boundary check. And in the 1 in 16 odds that it passes the boundary check, it will not decrypt properly as each segment's padding will be treated as actual data, and not padding. --- devine/core/manifests/hls.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/devine/core/manifests/hls.py b/devine/core/manifests/hls.py index 4af583d..946acc3 100644 --- a/devine/core/manifests/hls.py +++ b/devine/core/manifests/hls.py @@ -387,15 +387,27 @@ def decrypt(include_this_segment: bool) -> Path: elif len(files) != range_len: raise ValueError(f"Missing {range_len - len(files)} segment files for {segment_range}...") - merge( - to=merged_path, - via=files, - delete=True, - include_map_data=True - ) - - drm.decrypt(merged_path) - merged_path.rename(decrypted_path) + if isinstance(drm, Widevine): + # with widevine we can merge all segments and decrypt once + merge( + to=merged_path, + via=files, + delete=True, + include_map_data=True + ) + drm.decrypt(merged_path) + merged_path.rename(decrypted_path) + else: + # with other drm we must decrypt separately and then merge them + # for aes this is because each segment likely has 16-byte padding + for file in files: + drm.decrypt(file) + merge( + to=merged_path, + via=files, + delete=True, + include_map_data=True + ) events.emit( events.Types.TRACK_DECRYPTED,