cookiecloud-decrypt 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,145 @@
1
+ Metadata-Version: 2.4
2
+ Name: cookiecloud_decrypt
3
+ Version: 0.1.0
4
+ Summary: CookieCloud data decryption SDK — supports legacy and aes-128-cbc-fixed modes
5
+ Author-email: cky <cky@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/YOUR_USERNAME/cookiecloud_decrypt
8
+ Project-URL: Repository, https://github.com/YOUR_USERNAME/cookiecloud_decrypt
9
+ Keywords: cookiecloud,cookie,decrypt,web-scraping
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Internet :: WWW/HTTP
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: pycryptodome>=3.19
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=8.0; extra == "dev"
22
+ Requires-Dist: pytest-vcr>=1.0; extra == "dev"
23
+ Requires-Dist: httpx>=0.27; extra == "dev"
24
+ Requires-Dist: python-dotenv>=1.0; extra == "dev"
25
+
26
+ # cookiecloud_decrypt
27
+
28
+ CookieCloud 数据解密 SDK,纯 Python,无其他运行时依赖。
29
+
30
+ 支持两种加密模式:
31
+
32
+ - **aes-128-cbc-fixed**:新版 CookieCloud 服务器(MD5(key) + 零 IV)
33
+ - **legacy**:旧版使用(MD5 chain + Salted__ header,与 pycookiecloud 兼容)
34
+
35
+ ## 安装
36
+
37
+ ```bash
38
+ pip install cookiecloud_decrypt
39
+ ```
40
+
41
+ ## 快速开始
42
+
43
+ ```python
44
+ from cookiecloud_decrypt import decrypt, to_playwright_cookies, to_cookie_str
45
+
46
+ # 1. 从 CookieCloud 服务端获取加密数据(任意 HTTP 库)
47
+ import httpx
48
+ resp = httpx.get(
49
+ "https://your-server.com/get/YOUR_UUID",
50
+ params={"password": "YOUR_PASSWORD"},
51
+ )
52
+ encrypted = resp.json()["encrypted"]
53
+
54
+ # 2. 解密(auto 模式自动识别格式)
55
+ data = decrypt(encrypted, uuid="YOUR_UUID", password="YOUR_PASSWORD")
56
+
57
+ # 3. 格式化输出
58
+ # Playwright add_cookies 格式
59
+ cookies = to_playwright_cookies(data, domains=[".example.com"])
60
+
61
+ # "k1=v1; k2=v2" 字符串格式
62
+ cookie_str = to_cookie_str(data, domain=".example.com", keys=["a1", "session"])
63
+ ```
64
+
65
+ ## API
66
+
67
+ ### `decrypt(encrypted, uuid, password, mode="auto")`
68
+
69
+ 解密 CookieCloud 服务端返回的加密数据。
70
+
71
+ | 参数 | 类型 | 说明 |
72
+ |------|------|------|
73
+ | `encrypted` | `str` | CookieCloud GET `/get/{uuid}` 返回的 `encrypted` 字段(base64) |
74
+ | `uuid` | `str` | CookieCloud UUID |
75
+ | `password` | `str` | CookieCloud 密码 |
76
+ | `mode` | `str` | 解密模式:`"auto"`(默认,自动检测)、`"legacy"`、`"aes-128-cbc-fixed"` |
77
+
78
+ **返回值**:解密后的 dict,结构为 `{"cookie_data": {域名: [CookieEntry, ...]}, "update_time": ...}`
79
+
80
+ **异常**:
81
+ - `InvalidKeyError` — UUID 或密码错误
82
+ - `CorruptedDataError` — 密文格式损坏
83
+
84
+ ### `to_playwright_cookies(data, domains=None)`
85
+
86
+ 导出为 Playwright `BrowserContext.add_cookies()` 所需格式。
87
+
88
+ ```python
89
+ from playwright.async_api import async_playwright
90
+
91
+ cookies = to_playwright_cookies(data, domains=[".xiaohongshu.com"])
92
+ async with async_playwright() as p:
93
+ ctx = await p.chromium.launch()
94
+ await ctx.add_cookies(cookies)
95
+ ```
96
+
97
+ ### `to_cookie_str(data, domain, keys=None)`
98
+
99
+ 导出为 `k1=v1; k2=v2` 格式的字符串。
100
+
101
+ ```python
102
+ cookie_str = to_cookie_str(
103
+ data,
104
+ domain=".xiaohongshu.com",
105
+ keys=["a1", "web_session"],
106
+ )
107
+ # → "a1=xxx; web_session=yyy"
108
+ ```
109
+
110
+ ### `to_cookie_dict(data, domains=None)`
111
+
112
+ 导出为 `{域名: [CookieEntry, ...]}` 格式。
113
+
114
+ ## 开发
115
+
116
+ ```bash
117
+ # 安装
118
+ pip install -e ".[dev]"
119
+
120
+ # 运行测试
121
+ pytest tests/ -v
122
+
123
+ # 端到端测试(首次需要联网录制 cassette)
124
+ cp .env.example .env
125
+ # 填入真实 COOKIECLOUD_SERVER / COOKIECLOUD_UUID / COOKIECLOUD_PASSWORD
126
+ pytest tests/e2e_test.py -v --vcr-record=new_episodes
127
+ ```
128
+
129
+ ## 发布到 PyPI
130
+
131
+ ```bash
132
+ # 1. 安装构建工具
133
+ pip install build twine
134
+
135
+ # 2. 构建 wheel 和 sdist
136
+ python -m build
137
+
138
+ # 3. 上传到 TestPyPI(先测)
139
+ twine upload --repository testpypi dist/*
140
+
141
+ # 4. 上传到正式 PyPI
142
+ twine upload dist/*
143
+ ```
144
+
145
+ 发布后外部用户直接 `pip install cookiecloud_decrypt` 即可使用。
@@ -0,0 +1,120 @@
1
+ # cookiecloud_decrypt
2
+
3
+ CookieCloud 数据解密 SDK,纯 Python,无其他运行时依赖。
4
+
5
+ 支持两种加密模式:
6
+
7
+ - **aes-128-cbc-fixed**:新版 CookieCloud 服务器(MD5(key) + 零 IV)
8
+ - **legacy**:旧版使用(MD5 chain + Salted__ header,与 pycookiecloud 兼容)
9
+
10
+ ## 安装
11
+
12
+ ```bash
13
+ pip install cookiecloud_decrypt
14
+ ```
15
+
16
+ ## 快速开始
17
+
18
+ ```python
19
+ from cookiecloud_decrypt import decrypt, to_playwright_cookies, to_cookie_str
20
+
21
+ # 1. 从 CookieCloud 服务端获取加密数据(任意 HTTP 库)
22
+ import httpx
23
+ resp = httpx.get(
24
+ "https://your-server.com/get/YOUR_UUID",
25
+ params={"password": "YOUR_PASSWORD"},
26
+ )
27
+ encrypted = resp.json()["encrypted"]
28
+
29
+ # 2. 解密(auto 模式自动识别格式)
30
+ data = decrypt(encrypted, uuid="YOUR_UUID", password="YOUR_PASSWORD")
31
+
32
+ # 3. 格式化输出
33
+ # Playwright add_cookies 格式
34
+ cookies = to_playwright_cookies(data, domains=[".example.com"])
35
+
36
+ # "k1=v1; k2=v2" 字符串格式
37
+ cookie_str = to_cookie_str(data, domain=".example.com", keys=["a1", "session"])
38
+ ```
39
+
40
+ ## API
41
+
42
+ ### `decrypt(encrypted, uuid, password, mode="auto")`
43
+
44
+ 解密 CookieCloud 服务端返回的加密数据。
45
+
46
+ | 参数 | 类型 | 说明 |
47
+ |------|------|------|
48
+ | `encrypted` | `str` | CookieCloud GET `/get/{uuid}` 返回的 `encrypted` 字段(base64) |
49
+ | `uuid` | `str` | CookieCloud UUID |
50
+ | `password` | `str` | CookieCloud 密码 |
51
+ | `mode` | `str` | 解密模式:`"auto"`(默认,自动检测)、`"legacy"`、`"aes-128-cbc-fixed"` |
52
+
53
+ **返回值**:解密后的 dict,结构为 `{"cookie_data": {域名: [CookieEntry, ...]}, "update_time": ...}`
54
+
55
+ **异常**:
56
+ - `InvalidKeyError` — UUID 或密码错误
57
+ - `CorruptedDataError` — 密文格式损坏
58
+
59
+ ### `to_playwright_cookies(data, domains=None)`
60
+
61
+ 导出为 Playwright `BrowserContext.add_cookies()` 所需格式。
62
+
63
+ ```python
64
+ from playwright.async_api import async_playwright
65
+
66
+ cookies = to_playwright_cookies(data, domains=[".xiaohongshu.com"])
67
+ async with async_playwright() as p:
68
+ ctx = await p.chromium.launch()
69
+ await ctx.add_cookies(cookies)
70
+ ```
71
+
72
+ ### `to_cookie_str(data, domain, keys=None)`
73
+
74
+ 导出为 `k1=v1; k2=v2` 格式的字符串。
75
+
76
+ ```python
77
+ cookie_str = to_cookie_str(
78
+ data,
79
+ domain=".xiaohongshu.com",
80
+ keys=["a1", "web_session"],
81
+ )
82
+ # → "a1=xxx; web_session=yyy"
83
+ ```
84
+
85
+ ### `to_cookie_dict(data, domains=None)`
86
+
87
+ 导出为 `{域名: [CookieEntry, ...]}` 格式。
88
+
89
+ ## 开发
90
+
91
+ ```bash
92
+ # 安装
93
+ pip install -e ".[dev]"
94
+
95
+ # 运行测试
96
+ pytest tests/ -v
97
+
98
+ # 端到端测试(首次需要联网录制 cassette)
99
+ cp .env.example .env
100
+ # 填入真实 COOKIECLOUD_SERVER / COOKIECLOUD_UUID / COOKIECLOUD_PASSWORD
101
+ pytest tests/e2e_test.py -v --vcr-record=new_episodes
102
+ ```
103
+
104
+ ## 发布到 PyPI
105
+
106
+ ```bash
107
+ # 1. 安装构建工具
108
+ pip install build twine
109
+
110
+ # 2. 构建 wheel 和 sdist
111
+ python -m build
112
+
113
+ # 3. 上传到 TestPyPI(先测)
114
+ twine upload --repository testpypi dist/*
115
+
116
+ # 4. 上传到正式 PyPI
117
+ twine upload dist/*
118
+ ```
119
+
120
+ 发布后外部用户直接 `pip install cookiecloud_decrypt` 即可使用。
@@ -0,0 +1,61 @@
1
+ """
2
+ cookiecloud_decrypt
3
+ ~~~~~~~~~~~~~~~~~~
4
+
5
+ CookieCloud 数据解密 SDK。
6
+
7
+ 支持两种加密模式:
8
+
9
+ - **aes-128-cbc-fixed**: 新版 CookieCloud 服务器(MD5(key) + 零 IV)
10
+ - **legacy**: 旧版(MD5 chain + Salted__ header,兼容 pycookiecloud)
11
+
12
+ 用法::
13
+
14
+ from cookiecloud_decrypt import decrypt, to_playwright_cookies, to_cookie_str
15
+
16
+ # 解密
17
+ data = decrypt(encrypted="BASE64STRING...", uuid="...", password="...")
18
+
19
+ # Playwright 格式
20
+ cookies = to_playwright_cookies(data, domains=[".example.com"])
21
+
22
+ # "k1=v1; k2=v2" 格式
23
+ cookie_str = to_cookie_str(data, domain="example.com", keys=["a1", "b2"])
24
+ """
25
+
26
+ from .decrypt import (
27
+ decrypt,
28
+ encrypt_legacy,
29
+ encrypt_aes_128_cbc_fixed,
30
+ DecryptMode,
31
+ CookieCloudDecryptError,
32
+ InvalidKeyError,
33
+ CorruptedDataError,
34
+ )
35
+ from .format import (
36
+ to_cookie_dict,
37
+ to_playwright_cookies,
38
+ to_cookie_str,
39
+ )
40
+ from .models import (
41
+ CookieEntry,
42
+ CookieCloudData,
43
+ )
44
+
45
+ __all__ = [
46
+ # 解密
47
+ "decrypt",
48
+ "encrypt_legacy",
49
+ "encrypt_aes_128_cbc_fixed",
50
+ "DecryptMode",
51
+ "CookieCloudDecryptError",
52
+ "InvalidKeyError",
53
+ "CorruptedDataError",
54
+ # 格式化
55
+ "to_cookie_dict",
56
+ "to_playwright_cookies",
57
+ "to_cookie_str",
58
+ # 类型
59
+ "CookieEntry",
60
+ "CookieCloudData",
61
+ ]
@@ -0,0 +1,309 @@
1
+ """
2
+ CookieCloud 数据解密核心实现。
3
+
4
+ 支持两种加密模式:
5
+ - aes-128-cbc-fixed: 新版 CookieCloud 服务器(MD5(key) + 零 IV)
6
+ - legacy: 旧版使用(MD5 chain + Salted__ header,兼容 pycookiecloud)
7
+
8
+ 加密流程由 CookieCloud 服务端完成,参考:
9
+ https://github.com/easychen/CookieCloud
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import base64
15
+ import hashlib
16
+ import json
17
+ from enum import Enum
18
+ from typing import Any
19
+
20
+ from Cryptodome.Cipher import AES
21
+ from Cryptodome.Util.Padding import unpad
22
+
23
+
24
+ class DecryptMode(str, Enum):
25
+ """解密模式。"""
26
+ LEGACY = "legacy"
27
+ """旧版模式:MD5 chain + Salted__ header,与 pycookiecloud 兼容。"""
28
+ AES_128_CBC_FIXED = "aes-128-cbc-fixed"
29
+ """新版模式:MD5(key) + 零 IV。"""
30
+ AUTO = "auto"
31
+ """自动检测:优先尝试 aes-128-cbc-fixed,再尝试 legacy。"""
32
+
33
+
34
+ class CookieCloudDecryptError(Exception):
35
+ """解密失败基类。"""
36
+ pass
37
+
38
+
39
+ class InvalidKeyError(CookieCloudDecryptError):
40
+ """密钥错误(密码/UUID 不正确)。"""
41
+ pass
42
+
43
+
44
+ class CorruptedDataError(CookieCloudDecryptError):
45
+ """数据损坏(密文格式异常)。"""
46
+ pass
47
+
48
+
49
+ # ---------------------------------------------------------------------------
50
+ # 内部工具
51
+ # ---------------------------------------------------------------------------
52
+
53
+ SALTED_HEADER = b"Salted__"
54
+ """OpenSSL salted format header (legacy 模式)。"""
55
+
56
+
57
+ def _derive_key_md5(data: bytes, salt: bytes, output: int = 48) -> bytes:
58
+ """
59
+ MD5 chain key derivation — legacy 模式使用。
60
+ 等价于 OpenSSL EVP_BytesToKey(MD5)。
61
+
62
+ Args:
63
+ data: 初始种子(通常是 key + salt)
64
+ salt: 8 字节盐
65
+ output: 需要输出的总字节数
66
+
67
+ Returns:
68
+ key || iv (前 32 字节为 key,后 16 字节为 IV)
69
+ """
70
+ result = b""
71
+ prev = b""
72
+ while len(result) < output:
73
+ prev = hashlib.md5(prev + data + salt).digest()
74
+ result += prev
75
+ return result[:output]
76
+
77
+
78
+ def _derive_key_fixed(key: str) -> bytes:
79
+ """
80
+ MD5(key) key derivation — aes-128-cbc-fixed 模式使用。
81
+
82
+ Args:
83
+ key: UUID + '-' + password 的 MD5 十六进制字符串的前 16 字符
84
+
85
+ Returns:
86
+ 16 字节密钥
87
+ """
88
+ return hashlib.md5(key.encode()).digest()
89
+
90
+
91
+ # ---------------------------------------------------------------------------
92
+ # 加密函数(测试用,生产环境不需要)
93
+ # ---------------------------------------------------------------------------
94
+
95
+ def encrypt_legacy(plaintext: str, uuid: str, password: str) -> str:
96
+ """
97
+ 用 legacy 模式加密数据(用于生成测试 fixture)。
98
+ 流程与 CookieCloud 服务端一致。
99
+
100
+ Args:
101
+ plaintext: JSON 字符串
102
+ uuid: CookieCloud UUID
103
+ password: CookieCloud 密码
104
+
105
+ Returns:
106
+ base64 编码后的密文
107
+ """
108
+ import os
109
+
110
+ key_str = f"{uuid}-{password}"
111
+ hash_hex = hashlib.md5(key_str.encode()).hexdigest()
112
+ key = hash_hex[:16].encode()
113
+
114
+ salt = os.urandom(8)
115
+ key_iv = _derive_key_md5(key, salt, output=48)
116
+ aes_key, aes_iv = key_iv[:32], key_iv[32:]
117
+
118
+ padded = _pad(plaintext.encode("utf-8"))
119
+ cipher = AES.new(aes_key, AES.MODE_CBC, aes_iv)
120
+ ciphertext = cipher.encrypt(padded)
121
+
122
+ return base64.b64encode(b"Salted__" + salt + ciphertext).decode()
123
+
124
+
125
+ def encrypt_aes_128_cbc_fixed(plaintext: str, uuid: str, password: str) -> str:
126
+ """
127
+ 用 aes-128-cbc-fixed 模式加密数据(用于生成测试 fixture)。
128
+
129
+ Args:
130
+ plaintext: JSON 字符串
131
+ uuid: CookieCloud UUID
132
+ password: CookieCloud 密码
133
+
134
+ Returns:
135
+ base64 编码后的密文
136
+ """
137
+ import os
138
+
139
+ key_str = f"{uuid}-{password}"
140
+ hash_hex = hashlib.md5(key_str.encode()).hexdigest()
141
+ key = hash_hex[:16].encode()
142
+
143
+ aes_key = _derive_key_fixed(key_str)
144
+ zero_iv = b"\x00" * 16
145
+
146
+ padded = _pad(plaintext.encode("utf-8"))
147
+ cipher = AES.new(aes_key, AES.MODE_CBC, zero_iv)
148
+ ciphertext = cipher.encrypt(padded)
149
+
150
+ return base64.b64encode(ciphertext).decode()
151
+
152
+
153
+ def _pad(data: bytes) -> bytes:
154
+ """PKCS#7 padding。"""
155
+ block_size = 16
156
+ pad_len = block_size - (len(data) % block_size)
157
+ return data + bytes([pad_len] * pad_len)
158
+
159
+
160
+ # ---------------------------------------------------------------------------
161
+ # 解密函数
162
+ # ---------------------------------------------------------------------------
163
+
164
+ def _decrypt_legacy(encrypted_b64: str, uuid: str, password: str) -> dict[str, Any]:
165
+ """
166
+ 用 legacy 模式解密。
167
+ 与 pycookiecloud PyCryptoJS.py 的 decrypt() 完全兼容。
168
+
169
+ Raises:
170
+ CorruptedDataError: 密文格式损坏(Salted__ header 不匹配)
171
+ InvalidKeyError: 密钥错误(解密后不是合法 JSON)
172
+ """
173
+ try:
174
+ encrypted = base64.b64decode(encrypted_b64)
175
+ except Exception as e:
176
+ raise CorruptedDataError(f"base64 decode failed: {e}") from e
177
+
178
+ if not encrypted.startswith(SALTED_HEADER):
179
+ raise CorruptedDataError(
180
+ f"Missing OpenSSL salted header. Expected {SALTED_HEADER!r}, "
181
+ f"got {encrypted[:8]!r}. Data may use aes-128-cbc-fixed mode instead."
182
+ )
183
+
184
+ salt = encrypted[8:16]
185
+ ciphertext = encrypted[16:]
186
+
187
+ key_str = f"{uuid}-{password}"
188
+ hash_hex = hashlib.md5(key_str.encode()).hexdigest()
189
+ key = hash_hex[:16].encode()
190
+
191
+ key_iv = _derive_key_md5(key, salt, output=48)
192
+ aes_key, aes_iv = key_iv[:32], key_iv[32:]
193
+
194
+ try:
195
+ cipher = AES.new(aes_key, AES.MODE_CBC, aes_iv)
196
+ padded = cipher.decrypt(ciphertext)
197
+ plaintext = unpad(padded, AES.block_size)
198
+ return json.loads(plaintext.decode("utf-8"))
199
+ except (ValueError, json.JSONDecodeError) as e:
200
+ raise InvalidKeyError(
201
+ f"Decryption failed — wrong key or corrupted data. "
202
+ f"Verify uuid and password are correct. Cause: {e}"
203
+ ) from e
204
+
205
+
206
+ def _decrypt_aes_128_cbc_fixed(encrypted_b64: str, uuid: str, password: str) -> dict[str, Any]:
207
+ """
208
+ 用 aes-128-cbc-fixed 模式解密。
209
+ 新版 CookieCloud 服务器使用此模式。
210
+
211
+ Raises:
212
+ CorruptedDataError: 密文格式损坏(长度不是 16 的倍数)
213
+ InvalidKeyError: 密钥错误(解密后不是合法 JSON)
214
+ """
215
+ try:
216
+ encrypted = base64.b64decode(encrypted_b64)
217
+ except Exception as e:
218
+ raise CorruptedDataError(f"base64 decode failed: {e}") from e
219
+
220
+ if len(encrypted) % 16 != 0:
221
+ raise CorruptedDataError(
222
+ f"Encrypted data length ({len(encrypted)}) is not a multiple of 16. "
223
+ f"Data may use legacy mode instead."
224
+ )
225
+
226
+ key_str = f"{uuid}-{password}"
227
+ aes_key = _derive_key_fixed(key_str)
228
+ zero_iv = b"\x00" * 16
229
+
230
+ try:
231
+ cipher = AES.new(aes_key, AES.MODE_CBC, zero_iv)
232
+ padded = cipher.decrypt(encrypted)
233
+ plaintext = unpad(padded, AES.block_size)
234
+ return json.loads(plaintext.decode("utf-8"))
235
+ except (ValueError, json.JSONDecodeError) as e:
236
+ # ValueError 可能是:1) wrong key(解密内容非合法 PKCS#7),
237
+ # 2) wrong format(legacy 数据用 fixed 模式解密)。
238
+ # 用 Salted__ header 区分:legacy 格式有 header,fixed 没有。
239
+ # 若有 header 说明是 legacy 格式未匹配,转 CorruptedDataError 以便 auto fallback;
240
+ # 否则视为密钥错误。
241
+ if encrypted.startswith(SALTED_HEADER):
242
+ raise CorruptedDataError(
243
+ f"Data has OpenSSL salted header — appears to be legacy format. "
244
+ f"Retry with mode='legacy'."
245
+ ) from e
246
+ raise InvalidKeyError(
247
+ f"Decryption failed — wrong key or corrupted data. "
248
+ f"Verify uuid and password are correct. Cause: {e}"
249
+ ) from e
250
+
251
+
252
+ def decrypt(
253
+ encrypted: str,
254
+ uuid: str,
255
+ password: str,
256
+ mode: DecryptMode | str = DecryptMode.AUTO,
257
+ ) -> dict[str, Any]:
258
+ """
259
+ 解密 CookieCloud 服务端返回的加密数据。
260
+
261
+ Args:
262
+ encrypted: CookieCloud GET /get/{uuid} 返回的 ``encrypted`` 字段(base64)
263
+ uuid: CookieCloud UUID
264
+ password: CookieCloud 密码
265
+ mode: 解密模式,默认 auto(自动检测)
266
+
267
+ - ``"auto"``: 优先尝试 aes-128-cbc-fixed,再尝试 legacy
268
+ - ``"legacy"``: 仅使用 legacy 模式(兼容 pycookiecloud)
269
+ - ``"aes-128-cbc-fixed"``: 仅使用新版模式
270
+
271
+ Returns:
272
+ 解密后的原始 JSON dict(含 ``cookie_data`` 等顶层字段)
273
+
274
+ Raises:
275
+ CookieCloudDecryptError: 解密失败
276
+ InvalidKeyError: 密钥错误(uuid 或 password 不正确)
277
+ CorruptedDataError: 密文格式损坏
278
+
279
+ Example::
280
+
281
+ from cookiecloud_decrypt import decrypt
282
+
283
+ data = decrypt(
284
+ encrypted="BASE64STRING...",
285
+ uuid="your-uuid",
286
+ password="your-password",
287
+ )
288
+ cookie_dict = data["cookie_data"]
289
+ """
290
+ if isinstance(mode, str):
291
+ mode = DecryptMode(mode.lower())
292
+
293
+ if mode == DecryptMode.AUTO:
294
+ # 优先尝试 aes-128-cbc-fixed(新版)
295
+ try:
296
+ return _decrypt_aes_128_cbc_fixed(encrypted, uuid, password)
297
+ except CorruptedDataError:
298
+ # 不是 fixed 格式,尝试 legacy
299
+ return _decrypt_legacy(encrypted, uuid, password)
300
+ # InvalidKeyError 直接抛出,不 fallback(密钥错误时 legacy 也会失败)
301
+
302
+ elif mode == DecryptMode.AES_128_CBC_FIXED:
303
+ return _decrypt_aes_128_cbc_fixed(encrypted, uuid, password)
304
+
305
+ elif mode == DecryptMode.LEGACY:
306
+ return _decrypt_legacy(encrypted, uuid, password)
307
+
308
+ else:
309
+ raise ValueError(f"Unknown decrypt mode: {mode}")