Reduce Fernet per-operation overhead (#15357)
* Reduce Fernet per-operation overhead
- Hoist the PKCS7(128) and SHA256() singletons to module level and cache
the AES algorithm object on the Fernet instance instead of rebuilding
all three (plus the deprecated-module attribute lookups they involve)
on every encrypt/decrypt.
- MultiFernet.decrypt/decrypt_at_time/extract_timestamp now base64-decode
and parse the token once, then try each key against the parsed data
(mirroring what rotate() already did) instead of re-decoding the whole
token for every key.
- Use memoryview slices for the HMAC region and ciphertext instead of
copying (data[:-32] alone is a full copy per verify).
Benchmarks (Linux x86-64, CPython 3.11, release build, min of 7 timeit
repeats):
Fernet.encrypt (64B): 15712 ns -> 12364 ns
Fernet.decrypt (64B): 14245 ns -> 11221 ns
MultiFernet (3 keys, oldest matches) decrypt of 1MiB token:
12.76 ms -> 6.61 ms (1.9x)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5bsKENedjVD9gcLApf1x4
* Cover MultiFernet.decrypt ttl path and extract_timestamp bad-signature path
MultiFernet.decrypt now computes time_info itself, and
extract_timestamp's terminal InvalidToken is only reachable with a
well-framed token whose signature matches no key (framing-invalid tokens
now raise during the initial parse). Neither path had a test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5bsKENedjVD9gcLApf1x4
---------
Co-authored-by: Claude <noreply@anthropic.com>
2 files changed