using hashlib for PBKDF2

This commit is contained in:
Matthew Broadway 2021-06-03 21:59:32 +01:00
parent 5f323b1a94
commit 64c425364e
No known key found for this signature in database
GPG Key ID: DDC0B82B6896B381
2 changed files with 27 additions and 26 deletions

View File

@ -2,7 +2,7 @@ import unittest
from youtube_dl import cookies
from youtube_dl.cookies import LinuxChromeCookieDecryptor, WindowsChromeCookieDecryptor, CRYPTO_AVAILABLE, \
MacChromeCookieDecryptor
MacChromeCookieDecryptor, Logger
class MonkeyPatch:
@ -23,22 +23,22 @@ class MonkeyPatch:
@unittest.skipIf(not CRYPTO_AVAILABLE, 'cryptography library not available')
class TestCookies(unittest.TestCase):
def test_chrome_cookie_decryptor_linux_derive_key(self):
key = LinuxChromeCookieDecryptor.derive_key('abc')
key = LinuxChromeCookieDecryptor.derive_key(b'abc')
assert key == b'7\xa1\xec\xd4m\xfcA\xc7\xb19Z\xd0\x19\xdcM\x17'
def test_chrome_cookie_decryptor_mac_derive_key(self):
key = MacChromeCookieDecryptor.derive_key('abc')
key = MacChromeCookieDecryptor.derive_key(b'abc')
assert key == b'Y\xe2\xc0\xd0P\xf6\xf4\xe1l\xc1\x8cQ\xcb|\xcdY'
def test_chrome_cookie_decryptor_linux_v10(self):
with MonkeyPatch(cookies, '_get_linux_keyring_password', lambda *args, **kwargs: ''):
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
def test_chrome_cookie_decryptor_linux_v11(self):
with MonkeyPatch(cookies, '_get_linux_keyring_password', lambda *args, **kwargs: ''):
with MonkeyPatch(cookies, '_get_linux_keyring_password', lambda *args, **kwargs: b''):
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')
@ -49,11 +49,11 @@ class TestCookies(unittest.TestCase):
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('')
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: '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('')

View File

@ -1,4 +1,5 @@
import ctypes
import hashlib
import json
import os
import shutil
@ -12,8 +13,6 @@ from youtube_dl.utils import YoutubeDLCookieJar, expand_path
try:
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA1
CRYPTO_AVAILABLE = True
except ImportError:
CRYPTO_AVAILABLE = False
@ -25,7 +24,8 @@ except ImportError:
KEYRING_AVAILABLE = False
SUPPORTED_BROWSERS = ['firefox', 'chrome', 'chromium', 'brave', 'opera', 'edge']
SUPPORTED_BROWSERS = ['brave', 'chrome', 'chromium', 'edge' 'firefox', 'opera']
CHROME_LIKE_BROWSERS = {'brave', 'chrome', 'chromium', 'edge' 'opera'}
class Logger:
@ -46,7 +46,7 @@ def _is_path(value):
def extract_cookies_from_browser(browser_name, profile=None, logger=Logger()):
if browser_name == 'firefox':
return _extract_firefox_cookies(profile, logger)
elif browser_name in ('chrome', 'chromium', 'brave', 'opera', 'edge'):
elif browser_name in CHROME_LIKE_BROWSERS:
return _extract_chrome_cookies(browser_name, profile, logger)
else:
raise ValueError('unknown browser: {}'.format(browser_name))
@ -103,32 +103,32 @@ def _get_chrome_like_browser_settings(browser_name):
if sys.platform in ('linux', 'linux2'):
config = _config_home()
browser_dir = {
'brave': os.path.join(config, 'BraveSoftware/Brave-Browser'),
'chrome': os.path.join(config, 'google-chrome'),
'chromium': os.path.join(config, 'chromium'),
'brave': os.path.join(config, 'BraveSoftware/Brave-Browser'),
'opera': os.path.join(config, 'opera'),
'edge': os.path.join(config, 'microsoft-edge'),
'opera': os.path.join(config, 'opera'),
}[browser_name]
elif sys.platform == 'win32':
appdata_local = os.path.expandvars('%LOCALAPPDATA%')
appdata_roaming = os.path.expandvars('%APPDATA%')
browser_dir = {
'brave': os.path.join(appdata_local, r'BraveSoftware\Brave-Browser'),
'chrome': os.path.join(appdata_local, r'Google\Chrome'),
'chromium': os.path.join(appdata_local, r'Google\Chromium'),
'brave': os.path.join(appdata_local, r'BraveSoftware\Brave-Browser'),
'opera': os.path.join(appdata_roaming, r'Opera Software\Opera Stable'),
'edge': os.path.join(appdata_local, r'Microsoft\Edge'),
'opera': os.path.join(appdata_roaming, r'Opera Software\Opera Stable'),
}[browser_name]
elif sys.platform == 'darwin':
appdata = os.path.expanduser('~/Library/Application Support')
browser_dir = {
'brave': os.path.join(appdata, 'BraveSoftware/Brave-Browser'),
'chrome': os.path.join(appdata, 'Google/Chrome'),
'chromium': os.path.join(appdata, 'Google/Chromium'),
'brave': os.path.join(appdata, 'BraveSoftware/Brave-Browser'),
'opera': os.path.join(appdata, 'com.operasoftware.Opera'),
'edge': os.path.join(appdata, 'Microsoft Edge'),
'opera': os.path.join(appdata, 'com.operasoftware.Opera'),
}[browser_name]
else:
@ -137,11 +137,11 @@ def _get_chrome_like_browser_settings(browser_name):
# Linux keyring names can be determined by snooping on dbus while opening the browser in KDE:
# dbus-monitor "interface='org.kde.KWallet'" "type=method_return"
keyring_name = {
'brave': 'Brave',
'chrome': 'Chrome',
'chromium': 'Chromium',
'brave': 'Brave',
'opera': 'Opera' if sys.platform == 'darwin' else 'Chromium',
'edge': 'Mirosoft Edge' if sys.platform == 'darwin' else 'Chromium',
'opera': 'Opera' if sys.platform == 'darwin' else 'Chromium',
}[browser_name]
return {
@ -253,7 +253,7 @@ class LinuxChromeCookieDecryptor(ChromeCookieDecryptor):
self._v10_key = None
self._v11_key = None
if CRYPTO_AVAILABLE:
self._v10_key = self.derive_key('peanuts')
self._v10_key = self.derive_key(b'peanuts')
if KEYRING_AVAILABLE:
self._v11_key = self.derive_key(_get_linux_keyring_password(browser_keyring_name))
@ -261,7 +261,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 PBKDF2(password, salt=b'saltysalt', dkLen=16, count=1, hmac_hash_module=SHA1)
return hashlib.pbkdf2_hmac('sha1', password, salt=b'saltysalt', iterations=1, dklen=16)
def decrypt(self, encrypted_value):
version = encrypted_value[:3]
@ -293,7 +293,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 PBKDF2(password, salt=b'saltysalt', dkLen=16, count=1003, hmac_hash_module=SHA1)
return hashlib.pbkdf2_hmac('sha1', password, salt=b'saltysalt', iterations=1003, dklen=16)
def decrypt(self, encrypted_value):
version = encrypted_value[:3]
@ -358,12 +358,13 @@ def _get_linux_keyring_password(browser_keyring_name):
# this may be a bug as the intended behaviour is to generate a random password and store
# it, but that doesn't matter here.
password = ''
return password
return password.encode('utf-8')
def _get_mac_keyring_password(browser_keyring_name):
if KEYRING_AVAILABLE:
return keyring.get_password('{} Safe Storage'.format(browser_keyring_name), browser_keyring_name)
password = keyring.get_password('{} Safe Storage'.format(browser_keyring_name), browser_keyring_name)
return password.encode('utf-8')
else:
proc = subprocess.Popen(['security', 'find-generic-password',
'-w', # write password to stdout
@ -373,7 +374,7 @@ def _get_mac_keyring_password(browser_keyring_name):
stderr=subprocess.DEVNULL)
proc.wait()
if proc.returncode == 0:
return proc.stdout.read().decode('utf-8').strip()
return proc.stdout.read().strip()
else:
return None
@ -511,7 +512,7 @@ def parse_browser_specification(browser_specification):
if not parts[0] or len(parts) > 2:
raise ValueError('invalid browser specification: "{}"'.format(browser_specification))
browser_name, profile = parts
if _is_path(profile):
if profile is not None and _is_path(profile):
profile = os.path.expanduser(profile)
return browser_name, profile