from datetime import datetime, timedelta, timezone
from hashlib import pbkdf2_hmac
-from yt_dlp.aes import aes_cbc_decrypt
-from yt_dlp.compat import (
+from .aes import aes_cbc_decrypt_bytes, aes_gcm_decrypt_and_verify_bytes
+from .compat import (
compat_b64decode,
compat_cookiejar_Cookie,
)
-from yt_dlp.utils import (
+from .utils import (
bug_reports_message,
- bytes_to_intlist,
expand_path,
- intlist_to_bytes,
- process_communicate_or_kill,
+ Popen,
YoutubeDLCookieJar,
)
SQLITE_AVAILABLE = False
-try:
- from Crypto.Cipher import AES
- CRYPTO_AVAILABLE = True
-except ImportError:
- CRYPTO_AVAILABLE = False
-
try:
import keyring
KEYRING_AVAILABLE = True
raise FileNotFoundError('could not find firefox cookies database in {}'.format(search_root))
logger.debug('Extracting cookies from: "{}"'.format(cookie_database_path))
- with tempfile.TemporaryDirectory(prefix='youtube_dl') as tmpdir:
+ with tempfile.TemporaryDirectory(prefix='yt_dlp') as tmpdir:
cursor = None
try:
cursor = _open_database_copy(cookie_database_path, tmpdir)
decryptor = get_cookie_decryptor(config['browser_dir'], config['keyring_name'], logger)
- with tempfile.TemporaryDirectory(prefix='youtube_dl') as tmpdir:
+ with tempfile.TemporaryDirectory(prefix='yt_dlp') as tmpdir:
cursor = None
try:
cursor = _open_database_copy(cookie_database_path, tmpdir)
class MacChromeCookieDecryptor(ChromeCookieDecryptor):
def __init__(self, browser_keyring_name, logger):
self._logger = logger
- password = _get_mac_keyring_password(browser_keyring_name)
+ password = _get_mac_keyring_password(browser_keyring_name, logger)
self._v10_key = None if password is None else self.derive_key(password)
@staticmethod
if self._v10_key is None:
self._logger.warning('cannot decrypt v10 cookies: no key found', only_once=True)
return None
- elif not CRYPTO_AVAILABLE:
- self._logger.warning('cannot decrypt cookie as the `pycryptodome` module is not installed. '
- 'Please install by running `python3 -m pip install pycryptodome`',
- only_once=True)
- return None
# https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/os_crypt/os_crypt_win.cc
# kNonceLength
p.skip_to(value_offset)
value = p.read_cstring()
except UnicodeDecodeError:
- logger.warning('failed to parse cookie because UTF-8 decoding failed', only_once=True)
+ logger.warning('failed to parse Safari cookie because UTF-8 decoding failed', only_once=True)
return record_size
p.skip_to(record_size, 'space at the end of the record')
return password.encode('utf-8')
-def _get_mac_keyring_password(browser_keyring_name):
+def _get_mac_keyring_password(browser_keyring_name, logger):
if KEYRING_AVAILABLE:
+ logger.debug('using keyring to obtain password')
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
- '-a', browser_keyring_name, # match 'account'
- '-s', '{} Safe Storage'.format(browser_keyring_name)], # match 'service'
- stdout=subprocess.PIPE,
- stderr=subprocess.DEVNULL)
+ logger.debug('using find-generic-password to obtain password')
+ proc = Popen(
+ ['security', 'find-generic-password',
+ '-w', # write password to stdout
+ '-a', browser_keyring_name, # match 'account'
+ '-s', '{} Safe Storage'.format(browser_keyring_name)], # match 'service'
+ stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
try:
- stdout, stderr = process_communicate_or_kill(proc)
+ stdout, stderr = proc.communicate_or_kill()
+ if stdout[-1:] == b'\n':
+ stdout = stdout[:-1]
return stdout
- except BaseException:
+ except BaseException as e:
+ logger.warning(f'exception running find-generic-password: {type(e).__name__}({e})')
return None
if path is None:
logger.error('could not find local state file')
return None
- with open(path, 'r') as f:
+ with open(path, 'r', encoding='utf8') as f:
data = json.load(f)
try:
base64_key = data['os_crypt']['encrypted_key']
def _decrypt_aes_cbc(ciphertext, key, logger, initialization_vector=b' ' * 16):
- plaintext = aes_cbc_decrypt(bytes_to_intlist(ciphertext),
- bytes_to_intlist(key),
- bytes_to_intlist(initialization_vector))
+ plaintext = aes_cbc_decrypt_bytes(ciphertext, key, initialization_vector)
padding_length = plaintext[-1]
try:
- return intlist_to_bytes(plaintext[:-padding_length]).decode('utf-8')
+ return plaintext[:-padding_length].decode('utf-8')
except UnicodeDecodeError:
- logger.warning('failed to decrypt cookie because UTF-8 decoding failed. Possibly the key is wrong?', only_once=True)
+ logger.warning('failed to decrypt cookie (AES-CBC) because UTF-8 decoding failed. Possibly the key is wrong?', only_once=True)
return None
def _decrypt_aes_gcm(ciphertext, key, nonce, authentication_tag, logger):
- cipher = AES.new(key, AES.MODE_GCM, nonce)
try:
- plaintext = cipher.decrypt_and_verify(ciphertext, authentication_tag)
+ plaintext = aes_gcm_decrypt_and_verify_bytes(ciphertext, key, authentication_tag, nonce)
except ValueError:
- logger.warning('failed to decrypt cookie because the MAC check failed. Possibly the key is wrong?', only_once=True)
+ logger.warning('failed to decrypt cookie (AES-GCM) because the MAC check failed. Possibly the key is wrong?', only_once=True)
return None
try:
return plaintext.decode('utf-8')
except UnicodeDecodeError:
- logger.warning('failed to decrypt cookie because UTF-8 decoding failed. Possibly the key is wrong?', only_once=True)
+ logger.warning('failed to decrypt cookie (AES-GCM) because UTF-8 decoding failed. Possibly the key is wrong?', only_once=True)
return None
def _parse_browser_specification(browser_name, profile=None):
+ browser_name = browser_name.lower()
if browser_name not in SUPPORTED_BROWSERS:
raise ValueError(f'unsupported browser: "{browser_name}"')
if profile is not None and _is_path(profile):