YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
Tensorizer pre-authentication Argon2 work-factor amplification
Status: reproduced on Tensorizer v2.12.1,
commit 652d3c016c84836bba97153e108821c11428ac40.
Summary
An encrypted .tensors file controls the Argon2 opslimit and memlimit
values used by TensorDeserializer when a caller supplies a password through
DecryptionParams.from_string(). Tensorizer accepts work factors up to
4,294,967,295 operations and 4,398,046,510,080 bytes of memory. It runs the
attacker-selected key derivation before any tensor MAC can reject the modified
file.
A 262,440-byte file in this proof changes only the serialized values in the
20-byte PWHash parameter block; cmp reports three differing bytes. The
control requests 8 KiB and one operation. The trigger requests 256 MiB and
three operations. Across three fresh runs:
| Case | Exit | Peak RSS |
|---|---|---|
| Control 1 | 0 | 232,456,192 bytes |
| Trigger 1 | 1, after Argon2 and MAC rejection | 483,901,440 bytes |
| Control 2 | 0 | 231,702,528 bytes |
| Trigger 2 | 1, after Argon2 and MAC rejection | 468,877,312 bytes |
| Control 3 | 0 | 232,341,504 bytes |
| Trigger 3 | 1, after Argon2 and MAC rejection | 484,343,808 bytes |
The trigger adds approximately 237–252 MiB of resident memory before it is rejected. An attacker can request much larger valid work factors.
Root cause
tensorizer/_crypt_info.py:151-157decodes the file-controlledopslimitandmemlimitas unsigned 64-bit integers.tensorizer/serialization.py:1469-1480passes those values toEncryptionParams.from_string().tensorizer/_crypt/_encryption.py:523-537accepts the complete libsodium range, including a 4-TiB memory ceiling, without an application policy cap.tensorizer/serialization.py:3126-3130derives the key before_stream_decrypt()authenticates the tensor ciphertext.
The issue applies even when hash verification is enabled because key derivation necessarily occurs before the ciphertext MAC and tensor hashes are checked.
Reproduction
Install Tensorizer from the pinned commit and the dependencies in
requirements.txt, then run:
TENSORIZER_SOURCE=/path/to/tensorizer \
PYTHON_BIN=python3 \
./reproduce.sh
The script:
- serializes a four-element encrypted tensor using the minimum Argon2 work factors;
- creates a trigger by changing only the serialized PWHash parameters;
- deserializes both files three times in fresh child processes; and
- prints JSON containing file size, exit status, elapsed time, and peak RSS.
The control decrypts and prints sum=6.0. The trigger consumes the selected
Argon2 resources and is rejected only afterward with
Tensor decryption failed.
Security impact
Tensorizer documents loading models directly from HTTP, HTTPS, and S3. A malicious or replaced encrypted model can therefore impose attacker-selected memory and CPU work on an inference worker, model-conversion service, or other automated loader that uses a password. At the published maximums, a single small model can exhaust memory or monopolize CPU before authentication.
Prior-art distinction
Tensorizer issue #154 and PR #66 broadly disclose that metadata and headers
are not authenticated. They do not identify the PWHash work-factor fields as a
pre-authentication resource-exhaustion sink, demonstrate this attack, or
propose a work-factor cap. PR #208 concerns the separate tensor-header-length
allocation path.
Suggested remediation
Require caller-configured maximum KDF work factors during deserialization and reject file values above those limits before invoking Argon2. Conservative defaults should not exceed Tensorizer's documented interactive or moderate profiles. Authenticate the KDF parameter block through a trusted outer header or manifest where deployment architecture permits it.