Skip to content

Commit ff4daad

Browse files
ubejdullahsdaqo
andauthored
fix: decrypt AllAnime tobeparsed with either key (#335)
* fix: decrypt AllAnime tobeparsed with either key AllAnime encrypts the tobeparsed response with either the aaReq key (mask xor partB) or the static legacy key sha256("Xot36i3lK3:v1"), depending on the current rotation. decode_tobeparsed only tried the aaReq key, so it raised "MAC check failed" and crashed the whole program whenever the response used the legacy key. Now it tries both keys and use whichever authenticates, and fall back to returning no streams (dropping the cached crypto) instead of crashing if neither works. * version: bump --------- Co-authored-by: sdaqo <sdaqo.dev@protonmail.com>
1 parent 504bc51 commit ff4daad

5 files changed

Lines changed: 27 additions & 8 deletions

File tree

api/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "anipy-api"
3-
version = "3.8.14"
3+
version = "3.8.15"
44
description = "api for anipy-cli"
55
authors = ["sdaqo <sdaqo.dev@protonmail.com>"]
66
license = "GPL-3.0"

api/src/anipy_api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
__appname__ = "anipy-api"
2-
__version__ = "3.8.14"
2+
__version__ = "3.8.15"

api/src/anipy_api/provider/providers/allanime_provider.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class AllAnimeCrypto:
5757
FALLBACK_QUERY_HASH = (
5858
"d405d0edd690624b66baba3068e0edc3ac90f1597d898a1ec8db4e5c43c00fec"
5959
)
60+
# AllAnime encrypts the tobeparsed response with either the aaReq key or this static legacy key, depending on the rotation, so decoding tries both.
61+
RESPONSE_STATIC_KEY = hashlib.sha256(b"Xot36i3lK3:v1").digest()
6062

6163
def __init__(self, info_callback: Optional[Callable[[str], None]] = None):
6264
self._info: Callable[[str], None] = info_callback or (lambda message: None)
@@ -198,12 +200,24 @@ def build_source_request(self, session: Session) -> Tuple[str, str, bytes]:
198200
token = base64.b64encode(b"\x01" + iv + ciphertext + tag).decode()
199201
return query_hash, token, key
200202

203+
def invalidate(self):
204+
"""Drop the cached crypto so the next request refetches it."""
205+
self._cache = None
206+
201207
@staticmethod
202208
def decode_tobeparsed(tbp: str, key: bytes):
203209
raw = base64.b64decode(tbp)
204210
iv, ciphertext, tag = raw[1:13], raw[13:-16], raw[-16:]
205-
cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
206-
return json.loads(cipher.decrypt_and_verify(ciphertext, tag).decode("utf-8"))
211+
212+
# The response is signed with either the aaReq key or the static legacy key, so try both and use whichever authenticates.
213+
for candidate in (key, AllAnimeCrypto.RESPONSE_STATIC_KEY):
214+
try:
215+
cipher = AES.new(candidate, AES.MODE_GCM, nonce=iv)
216+
plain = cipher.decrypt_and_verify(ciphertext, tag)
217+
return json.loads(plain.decode("utf-8"))
218+
except ValueError:
219+
continue
220+
raise ValueError("tobeparsed could not be decrypted with any known key")
207221

208222

209223
class AllAnimeFilter(BaseFilter):
@@ -416,7 +430,12 @@ def get_video(
416430

417431
data = result.get("data") or {}
418432
if "tobeparsed" in data:
419-
data = self._crypto.decode_tobeparsed(data["tobeparsed"], key)
433+
try:
434+
data = self._crypto.decode_tobeparsed(data["tobeparsed"], key)
435+
except ValueError:
436+
# Crypto rotated between the aaReq and the response, drop the cache so the next attempt refetches, and return no streams instead of crashing.
437+
self._crypto.invalidate()
438+
return streams
420439

421440
if not data.get("episode"):
422441
return streams

cli/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "anipy-cli"
3-
version = "3.8.14"
3+
version = "3.8.15"
44
description = "Watch and Download anime from the comfort of your Terminal"
55
authors = ["sdaqo <sdaqo.dev@protonmail.com>"]
66
license = "GPL-3.0"
@@ -20,7 +20,7 @@ yaspin = "^3.0.2"
2020
inquirerpy = "^0.3.4"
2121
appdirs = "^1.4.4"
2222
pypresence = "^4.3.0"
23-
anipy-api = "^3.8.14"
23+
anipy-api = "^3.8.15"
2424

2525
[tool.poetry.scripts]
2626
anipy-cli = "anipy_cli.cli:run_cli"

cli/src/anipy_cli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
__appname__ = "anipy-cli"
2-
__version__ = "3.8.14"
2+
__version__ = "3.8.15"

0 commit comments

Comments
 (0)