switched to using pycryptodome as it is already an optional dependency of youtube_dl

This commit is contained in:
Matthew Broadway 2021-06-03 20:08:45 +01:00
parent 8291844201
commit 6d6fe8b3d0
No known key found for this signature in database
GPG Key ID: DDC0B82B6896B381
2 changed files with 40 additions and 29 deletions

View File

@ -22,6 +22,14 @@ 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')
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')
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: ''):
encrypted_value = b'v10\xccW%\xcd\xe6\xe6\x9fM" \xa7\xb0\xca\xe4\x07\xd6'

View File

@ -11,11 +11,9 @@ from youtube_dl.compat import compat_cookiejar_Cookie, compat_b64decode, Compat_
from youtube_dl.utils import YoutubeDLCookieJar, expand_path
try:
from cryptography.hazmat.primitives.ciphers import Cipher
from cryptography.hazmat.primitives.ciphers.algorithms import AES
from cryptography.hazmat.primitives.ciphers.modes import CBC, GCM
from cryptography.hazmat.primitives.hashes import SHA1
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA1
CRYPTO_AVAILABLE = True
except ImportError:
CRYPTO_AVAILABLE = False
@ -197,7 +195,7 @@ class ChromeCookieDecryptor:
Mac:
- cookies are either v10 or not v10
- v10: AES-CBC encrypted (with more iterations than Linux) with an OS protected key (keyring)
- v10: AES-CBC encrypted with an OS protected key (keyring) and more key derivation iterations than linux
- not v10: 'old data' stored as plaintext
Windows:
@ -232,15 +230,15 @@ class LinuxChromeCookieDecryptor(ChromeCookieDecryptor):
self._v10_key = None
self._v11_key = None
if CRYPTO_AVAILABLE:
# values from
# https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/os_crypt/os_crypt_linux.cc
self._v10_key = PBKDF2HMAC(algorithm=SHA1(), length=16,
salt=b'saltysalt', iterations=1).derive(b'peanuts')
self._v10_key = self.derive_key('peanuts')
if KEYRING_AVAILABLE:
password = _get_linux_keyring_password(browser_keyring_name)
self._v11_key = PBKDF2HMAC(algorithm=SHA1(), length=16,
salt=b'saltysalt', iterations=1).derive(password.encode('utf-8'))
self._v11_key = self.derive_key(_get_linux_keyring_password(browser_keyring_name))
@staticmethod
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)
def decrypt(self, encrypted_value):
version = encrypted_value[:3]
@ -248,13 +246,13 @@ class LinuxChromeCookieDecryptor(ChromeCookieDecryptor):
if version == b'v10':
if self._v10_key is None:
warnings.warn('cannot decrypt cookie as the cryptography module is not installed')
warnings.warn('cannot decrypt cookie as the module `pycryptodome` is not installed')
return None
return _decrypt_aes_cbc(ciphertext, self._v10_key)
elif version == b'v11':
if self._v11_key is None:
warnings.warn('cannot decrypt cookie as the cryptography or keyring modules are not installed')
warnings.warn('cannot decrypt cookie as the `pycryptodome` or `keyring` modules are not installed')
return None
return _decrypt_aes_cbc(ciphertext, self._v11_key)
@ -266,11 +264,13 @@ class MacChromeCookieDecryptor(ChromeCookieDecryptor):
def __init__(self, browser_keyring_name):
self._v10_key = None
if CRYPTO_AVAILABLE:
# values from
# https://chromium.googlesource.com/chromium/src/+/refs/heads/main/components/os_crypt/os_crypt_mac.mm
password = _get_mac_keyring_password(browser_keyring_name)
self._v10_key = PBKDF2HMAC(algorithm=SHA1(), length=16,
salt=b'saltysalt', iterations=1003).derive(password.encode('utf-8'))
self._v10_key = self.derive_key(_get_mac_keyring_password(browser_keyring_name))
@staticmethod
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)
def decrypt(self, encrypted_value):
version = encrypted_value[:3]
@ -278,7 +278,7 @@ class MacChromeCookieDecryptor(ChromeCookieDecryptor):
if version == b'v10':
if self._v10_key is None:
warnings.warn('cannot decrypt cookie as the cryptography module is not installed')
warnings.warn('cannot decrypt cookie as the `pycryptodome` module is not installed')
return None
return _decrypt_aes_cbc(ciphertext, self._v10_key)
@ -372,10 +372,9 @@ def _get_windows_v10_password(browser_root):
return _decrypt_windows_dpapi(encrypted_password[len(prefix):])
def _decrypt_aes_cbc(ciphertext, key):
cipher = Cipher(algorithm=AES(key), mode=CBC(initialization_vector=b' ' * 16))
decryptor = cipher.decryptor()
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
def _decrypt_aes_cbc(ciphertext, key, initialization_vector=b' ' * 16):
cipher = AES.new(key, AES.MODE_CBC, iv=initialization_vector)
plaintext = cipher.decrypt(ciphertext)
padding_length = compat_ord(plaintext[-1])
try:
return plaintext[:-padding_length].decode('utf-8')
@ -385,9 +384,13 @@ def _decrypt_aes_cbc(ciphertext, key):
def _decrypt_aes_gcm(ciphertext, key, nonce, authentication_tag):
cipher = Cipher(algorithm=AES(key), mode=GCM(nonce, tag=authentication_tag))
decryptor = cipher.decryptor()
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
cipher = AES.new(key, AES.MODE_GCM, nonce)
try:
plaintext = cipher.decrypt_and_verify(ciphertext, authentication_tag)
except ValueError:
warnings.warn('failed to decrypt cookie because the MAC check failed. Possibly the key is wrong?')
return None
try:
return plaintext.decode('utf-8')
except UnicodeDecodeError: