Summary
AutoSerializer's msgpack/dataframe/series paths never set compressed= on their metadata, so it defaults False even though Rust ByteStorage LZ4 ran. EncryptionWrapper binds str(metadata.compressed) into the AES-GCM AAD, so an EncryptionWrapper(serializer=AutoSerializer()) write authenticates "False" while StandardSerializer (the canonical path) authenticates "True" for the identical wire format.
Evidence
src/cachekit/serializers/auto_serializer.py:451 (compressed unset) + base.py:201 (default False) + :785 (LZ4 ran)
src/cachekit/serializers/encryption_wrapper.py:231,395 (binds str(compressed) into AAD v0x03)
- Canonical:
standard_serializer.py:296 sets compressed=True; protocol protocol/spec/encryption.md:285,303 uses true; tests/critical/test_aad_v03_security.py:66 asserts "True"
Impact
Intra-process round-trips fine (decrypt rebuilds the same wrong AAD). The hazard is cross-SDK / corrected-impl reads: a conformant reader computes a different AAD and AES-GCM auth fails on a legitimately stored entry. Gated behind the non-default direct EncryptionWrapper(serializer=AutoSerializer()) API (the decorator forces StandardSerializer). See cachekit-io/protocol#12 for the spec-side gap.
Fix
Set compressed consistently in AutoSerializer metadata (reflect the ByteStorage codec actually applied).
Summary
AutoSerializer's msgpack/dataframe/series paths never setcompressed=on their metadata, so it defaultsFalseeven though Rust ByteStorage LZ4 ran.EncryptionWrapperbindsstr(metadata.compressed)into the AES-GCM AAD, so anEncryptionWrapper(serializer=AutoSerializer())write authenticates"False"whileStandardSerializer(the canonical path) authenticates"True"for the identical wire format.Evidence
src/cachekit/serializers/auto_serializer.py:451(compressed unset) +base.py:201(default False) +:785(LZ4 ran)src/cachekit/serializers/encryption_wrapper.py:231,395(bindsstr(compressed)into AAD v0x03)standard_serializer.py:296setscompressed=True; protocolprotocol/spec/encryption.md:285,303usestrue;tests/critical/test_aad_v03_security.py:66asserts"True"Impact
Intra-process round-trips fine (decrypt rebuilds the same wrong AAD). The hazard is cross-SDK / corrected-impl reads: a conformant reader computes a different AAD and AES-GCM auth fails on a legitimately stored entry. Gated behind the non-default direct
EncryptionWrapper(serializer=AutoSerializer())API (the decorator forces StandardSerializer). See cachekit-io/protocol#12 for the spec-side gap.Fix
Set
compressedconsistently inAutoSerializermetadata (reflect the ByteStorage codec actually applied).