fix: verify each 3DS file once per content

check_crypto_validation keeps its answer keyed by the file and keys file as compute_hashes keys them (path, inode, size, mtime, ctime). verify --all ran the verifiers 35 times for 5 files (0.88 s), now 5 times (0.13 s). verify --all --json output is byte-identical.
This commit is contained in:
Abdessamad Derraz committed 2026-10-21 10:03:27 +02:00
1 parent 11c836cdee
commit d904116a79
2 files changed
+65 -3

No files matched your search

+25 -3
View File
@@ -490,6 +490,23 @@ _CRYPTO_VERIFIERS: dict[str, Callable] = {
}
def _stat_key(path: str | Path) -> tuple | None:
"""(path, inode, size, mtime_ns, ctime_ns): the file as compute_hashes
keys it, None when it cannot be read."""
try:
stat = Path(path).stat()
except OSError:
return None
return (str(Path(path).resolve()), stat.st_ino, stat.st_size,
stat.st_mtime_ns, stat.st_ctime_ns)
# (verifier, file key, keys file key) -> reason or None. verify --all ran the
# curve and RSA checks of the same five files once per platform, 35 runs for
# 5 answers: the answer depends on the file's bytes and the keys file alone.
_crypto_cache: dict[tuple, str | None] = {}
def check_crypto_validation(
local_path: str,
filename: str,
@@ -508,8 +525,13 @@ def check_crypto_validation(
if not keys_file:
return "crypto check skipped: no keys file found"
file_key, keys_key = _stat_key(local_path), _stat_key(keys_file)
key = (filename, file_key, keys_key) if file_key and keys_key else None
if key in _crypto_cache:
return _crypto_cache[key]
keys = parse_keys_file(keys_file)
valid, reason = verifier(local_path, keys)
if valid:
return None
return reason
result = None if valid else reason
if key is not None:
_crypto_cache[key] = result
return result
+40
View File
@@ -19,6 +19,7 @@ import shutil
import sys
import tempfile
import unittest
import unittest.mock
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
@@ -329,5 +330,44 @@ class KeysFile(unittest.TestCase):
self.assertIsNone(crypto_verify.find_keys_file(self.tmp))
class OneFileIsVerifiedOncePerContent(unittest.TestCase):
"""verify --all ran the 3DS checks of the same five files once per
platform: 35 runs of pure-Python curve and RSA work for 5 answers."""
def setUp(self):
self._tmp = tempfile.TemporaryDirectory()
self.bios = Path(self._tmp.name)
target = self.bios / "Nintendo" / "3DS"
target.mkdir(parents=True)
(target / "aes_keys.txt").write_text(":AES\n")
self.file = target / "otp.bin"
self.file.write_bytes(b"\x00" * 0x100)
self.calls: list[bytes] = []
def verifier(path, keys):
data = Path(path).read_bytes()
self.calls.append(data)
return data[0] == 1, "first byte is not 1"
patcher = unittest.mock.patch.dict(crypto_verify._CRYPTO_VERIFIERS, {"otp.bin": verifier})
patcher.start()
self.addCleanup(patcher.stop)
self.addCleanup(crypto_verify._crypto_cache.clear)
self.addCleanup(self._tmp.cleanup)
def check(self) -> str | None:
return crypto_verify.check_crypto_validation(str(self.file), "otp.bin", str(self.bios))
def test_the_same_file_is_verified_once(self):
self.assertEqual([self.check() for _ in range(7)], ["first byte is not 1"] * 7)
self.assertEqual(len(self.calls), 1)
def test_a_rewritten_file_is_verified_again(self):
self.assertEqual(self.check(), "first byte is not 1")
self.file.write_bytes(b"\x01" + b"\x00" * 0x1FF)
self.assertIsNone(self.check())
self.assertEqual(len(self.calls), 2)
if __name__ == "__main__":
unittest.main()