Skip to content

Support zip_fseek() for stored entries encrypted with WinZip AES - #584

Open
fdegros wants to merge 1 commit into
nih-at:mainfrom
fdegros:winzip-aes-seekable
Open

fdegros wants to merge 1 commit into
nih-at:mainfrom
fdegros:winzip-aes-seekable

Conversation

@fdegros

@fdegros fdegros commented Sep 23, 2026

Copy link
Copy Markdown

Summary

WinZip AES uses AES-CTR, which has no chaining dependency between blocks - decrypting block n only requires the counter value for that block, not any preceding ciphertext or plaintext. This adds _zip_winzip_aes_seek() to jump the cipher's internal counter/keystream state directly to an arbitrary byte offset, and wires ZIP_SOURCE_SEEK/ZIP_SOURCE_TELL into the WinZip AES decode source accordingly, so zip_fseek()/zip_file_is_seekable() now work for STOREd (uncompressed) entries encrypted with WinZip AES.

The one real constraint is the mandatory HMAC-SHA1 authentication code, which is computed over the ciphertext sequentially from the start and can't be verified for a chunk read out of order. This is handled exactly like zip_source_crc.c already handles the same situation for the CRC of a stored, unencrypted entry: the amount of ciphertext fed to the HMAC contiguously from position 0 is tracked, and verification is silently skipped (not failed) if a seek ever leaves a gap that's never filled in. A full, contiguous read from the start still verifies the HMAC exactly as before, including still catching a tampered file - only reads that actually skip around lose that guarantee, the same tradeoff already made for CRC.

Also updates zip_fseek()'s man page: it previously said compressed or encrypted data always errors out, which is no longer accurate.

Fixes #336.

Test plan

  • ctest (196/196 tests pass)
  • New regression tests: winzip_aes_seek.test, winzip_aes_seek_bad_hmac_full_read.test, winzip_aes_seek_bad_hmac_partial_read.test
  • Manual verification of correct decryption at arbitrary (including non-block-aligned) offsets, cross-checked against full sequential reads, both on synthetic fixtures and on a real ~1GB AES-256 entry created independently with 7z
  • Manual verification that a full sequential read of a file with a tampered authentication code still fails, while a seeked/partial read of the same tampered file succeeds silently (matching the documented, intentional tradeoff)
  • clang-format --dry-run --Werror clean on all touched C/H files
  • Man pages regenerated (.mdoc/.man/.html) for the touched page

🤖 Generated with Claude Code

WinZip AES uses AES-CTR, which has no chaining dependency between blocks -
decrypting block n only requires the counter value for that block, not any
preceding ciphertext or plaintext. Add _zip_winzip_aes_seek() to jump the
cipher's internal counter/keystream state directly to an arbitrary byte offset,
and wire ZIP_SOURCE_SEEK/ ZIP_SOURCE_TELL into the WinZip AES decode source
accordingly.

The one real constraint is the mandatory HMAC-SHA1 authentication code, which
is computed over the ciphertext sequentially from the start and can't be
verified for a chunk read out of order. Handle this exactly like
zip_source_crc.c already handles the same situation for the CRC of a stored,
unencrypted entry: track how much of the ciphertext has been fed to the HMAC
contiguously from position 0, and silently skip verification instead of failing
if a seek ever leaves a gap that's never filled in. A full, contiguous read
from the start still verifies the HMAC exactly as before, including still
catching a tampered file - only reads that actually skip around lose that
guarantee, the same tradeoff already made for CRC.

Also update zip_fseek()'s man page: it previously said compressed or encrypted
data always errors out, which is no longer accurate.

Fixes nih-at#336.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make encrypted-but-not-compressed files seekable

1 participant