added compatability wrapper for pbkdf2_sha1

This commit is contained in:
Matthew Broadway 2021-06-17 20:32:43 +01:00
parent 613fe995f5
commit 104af51a20
No known key found for this signature in database
GPG Key ID: DDC0B82B6896B381
2 changed files with 48 additions and 16 deletions

View File

@ -1,44 +1,51 @@
import sys
import unittest
from datetime import datetime, timezone
from youtube_dl import cookies
from youtube_dl.cookies import LinuxChromeCookieDecryptor, WindowsChromeCookieDecryptor, CRYPTO_AVAILABLE, \
MacChromeCookieDecryptor, Logger, parse_safari_cookies
MacChromeCookieDecryptor, Logger, parse_safari_cookies, pbkdf2_sha1, PBKDF2_AVAILABLE
class MonkeyPatch:
def __init__(self, module, name, temp_value):
def __init__(self, module, temporary_values):
self._module = module
self._name = name
self._temp_value = temp_value
self._backup_value = None
self._temporary_values = temporary_values
self._backup_values = {}
def __enter__(self):
self._backup_value = getattr(self._module, self._name)
setattr(self._module, self._name, self._temp_value)
for name, temp_value in self._temporary_values.items():
self._backup_values[name] = getattr(self._module, name)
setattr(self._module, name, temp_value)
def __exit__(self, exc_type, exc_val, exc_tb):
setattr(self._module, self._name, self._backup_value)
for name, backup_value in self._backup_values.items():
setattr(self._module, name, backup_value)
class TestCookies(unittest.TestCase):
@unittest.skipIf(not PBKDF2_AVAILABLE, 'PBKDF2 not available')
def test_chrome_cookie_decryptor_linux_derive_key(self):
key = LinuxChromeCookieDecryptor.derive_key(b'abc')
assert key == b'7\xa1\xec\xd4m\xfcA\xc7\xb19Z\xd0\x19\xdcM\x17'
@unittest.skipIf(not PBKDF2_AVAILABLE, 'PBKDF2 not available')
def test_chrome_cookie_decryptor_mac_derive_key(self):
key = MacChromeCookieDecryptor.derive_key(b'abc')
assert key == b'Y\xe2\xc0\xd0P\xf6\xf4\xe1l\xc1\x8cQ\xcb|\xcdY'
@unittest.skipIf(not PBKDF2_AVAILABLE, 'PBKDF2 not available')
def test_chrome_cookie_decryptor_linux_v10(self):
with MonkeyPatch(cookies, '_get_linux_keyring_password', lambda *args, **kwargs: b''):
with MonkeyPatch(cookies, {'_get_linux_keyring_password': lambda *args, **kwargs: b''}):
encrypted_value = b'v10\xccW%\xcd\xe6\xe6\x9fM" \xa7\xb0\xca\xe4\x07\xd6'
value = 'USD'
decryptor = LinuxChromeCookieDecryptor('Chrome')
assert decryptor.decrypt(encrypted_value) == value
@unittest.skipIf(not PBKDF2_AVAILABLE, 'PBKDF2 not available')
def test_chrome_cookie_decryptor_linux_v11(self):
with MonkeyPatch(cookies, '_get_linux_keyring_password', lambda *args, **kwargs: b''):
with MonkeyPatch(cookies, {'_get_linux_keyring_password': lambda *args, **kwargs: b'',
'KEYRING_AVAILABLE': True}):
encrypted_value = b'v11#\x81\x10>`w\x8f)\xc0\xb2\xc1\r\xf4\x1al\xdd\x93\xfd\xf8\xf8N\xf2\xa9\x83\xf1\xe9o\x0elVQd'
value = 'tz=Europe.London'
decryptor = LinuxChromeCookieDecryptor('Chrome')
@ -46,15 +53,16 @@ class TestCookies(unittest.TestCase):
@unittest.skipIf(not CRYPTO_AVAILABLE, 'cryptography library not available')
def test_chrome_cookie_decryptor_windows_v10(self):
with MonkeyPatch(cookies, '_get_windows_v10_key',
lambda *args, **kwargs: b'Y\xef\xad\xad\xeerp\xf0Y\xe6\x9b\x12\xc2<z\x16]\n\xbb\xb8\xcb\xd7\x9bA\xc3\x14e\x99{\xd6\xf4&'):
with MonkeyPatch(cookies, {
'_get_windows_v10_key': lambda *args, **kwargs: b'Y\xef\xad\xad\xeerp\xf0Y\xe6\x9b\x12\xc2<z\x16]\n\xbb\xb8\xcb\xd7\x9bA\xc3\x14e\x99{\xd6\xf4&'
}):
encrypted_value = b'v10T\xb8\xf3\xb8\x01\xa7TtcV\xfc\x88\xb8\xb8\xef\x05\xb5\xfd\x18\xc90\x009\xab\xb1\x893\x85)\x87\xe1\xa9-\xa3\xad='
value = '32101439'
decryptor = WindowsChromeCookieDecryptor('', Logger())
assert decryptor.decrypt(encrypted_value) == value
def test_chrome_cookie_decryptor_mac_v10(self):
with MonkeyPatch(cookies, '_get_mac_keyring_password', lambda *args, **kwargs: b'6eIDUdtKAacvlHwBVwvg/Q=='):
with MonkeyPatch(cookies, {'_get_mac_keyring_password': lambda *args, **kwargs: b'6eIDUdtKAacvlHwBVwvg/Q=='}):
encrypted_value = b'v10\xb3\xbe\xad\xa1[\x9fC\xa1\x98\xe0\x9a\x01\xd9\xcf\xbfc'
value = '2021-06-01-22'
decryptor = MacChromeCookieDecryptor('')
@ -80,3 +88,10 @@ class TestCookies(unittest.TestCase):
assert not cookie.secure
expected_expiration = datetime(2021, 6, 18, 20, 39, 19, tzinfo=timezone.utc)
assert cookie.expires == expected_expiration.timestamp()
def test_pbkdf2_sha1(self):
key = pbkdf2_sha1(b'peanuts', b' ' * 16, 1, 16)
if PBKDF2_AVAILABLE:
assert key == b'g\xe1\x8e\x0fQ\x1c\x9b\xf3\xc9`!\xaa\x90\xd9\xd34'
else:
assert key is None

View File

@ -1,5 +1,4 @@
import ctypes
import hashlib
import json
import os
import shutil
@ -294,7 +293,7 @@ class LinuxChromeCookieDecryptor(ChromeCookieDecryptor):
def derive_key(password):
# values from
# https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/os_crypt/os_crypt_linux.cc
return hashlib.pbkdf2_hmac('sha1', password, salt=b'saltysalt', iterations=1, dklen=16)
return pbkdf2_sha1(password, salt=b'saltysalt', iterations=1, key_length=16)
def decrypt(self, encrypted_value):
version = encrypted_value[:3]
@ -321,7 +320,7 @@ class MacChromeCookieDecryptor(ChromeCookieDecryptor):
def derive_key(password):
# values from
# https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/os_crypt/os_crypt_mac.mm
return hashlib.pbkdf2_hmac('sha1', password, salt=b'saltysalt', iterations=1003, dklen=16)
return pbkdf2_sha1(password, salt=b'saltysalt', iterations=1003, key_length=16)
def decrypt(self, encrypted_value):
version = encrypted_value[:3]
@ -590,6 +589,24 @@ def _get_windows_v10_key(browser_root, logger):
return _decrypt_windows_dpapi(encrypted_key[len(prefix):], logger)
PBKDF2_AVAILABLE = sys.version_info[:2] >= (3, 4) or CRYPTO_AVAILABLE
def pbkdf2_sha1(password, salt, iterations, key_length):
try:
from hashlib import pbkdf2_hmac
return pbkdf2_hmac('sha1', password, salt, iterations, key_length)
except ImportError:
try:
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA1
return PBKDF2(password, salt, key_length, iterations, hmac_hash_module=SHA1)
except ImportError:
warnings.warn('PBKDF2 is not available. You must either upgrade to '
'python >= 3.4 or install the pycryptodome package')
return None
def _decrypt_aes_cbc(ciphertext, key, initialization_vector=b' ' * 16):
plaintext = aes_cbc_decrypt(bytes_to_intlist(ciphertext),
bytes_to_intlist(key),