Repository navigation
Conversation
Stream decrypted bytes to the output file instead of buffering the whole payload, and validate the zip tail without copying it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Same goal as #102, but without the
_OleStreamReaderworkaround: this version relies on the lazyOleStreamfrom olefile (decalage2/olefile#177), soOOXMLFile.decrypt()readsEncryptedPackagedirectly viaopenstream().Closes #101
Problem
OOXMLFile.decrypt()materialized the entire decrypted OOXML package in memory and copied it several times: the agile/standard decrypt methods accumulated output in aBytesIOand returned a second copy, and the result was copied again into aBytesIOjust to runzipfile.is_zipfile. Peak RSS was ~4–5× the input size.Changes
msoffcrypto/method/ecma376_agile.py: extracted the 4096-byte segment loop into a streaming generatordecrypt_stream(...);decrypt(...)remains as a thinb"".join(...)wrapper (public API unchanged).msoffcrypto/method/ecma376_standard.py: addeddecrypt_stream(...)feeding AES-ECB incrementally in chunks and yielding exactlytotalSizeplaintext bytes;decrypt(...)unchanged.msoffcrypto/format/ooxml.py:OOXMLFile.decrypt()streams decrypted bytes tooutfilechunk-by-chunk; the post-decryption check now validates only the end-of-central-directory record in the last 65,557 bytes (_is_zipfile_tail) instead ofzipfile.is_zipfile(io.BytesIO(obuf)), so no full-payload copy is made. SameInvalidKeyErroron failure.tests/test_ooxml_streaming.py: streaming output is byte-identical to the buffereddecrypt(), works with a non-seekable output, and a wrong password still raisesInvalidKeyError.Compatibility note
This version intentionally omits the
_OleStreamReaderworkaround from #102 and expects the lazyOleStreamfrom decalage2/olefile#177 (currently unreleased). Once that lands, the minimumolefileversion inpyproject.tomlshould be bumped accordingly. Until then, users on released olefile (0.47) will still get the eagerly buffered stream — use #102 for a fix that works with any olefile version.Public API is unchanged:
OOXMLFile.decrypt(outfile),OfficeFile.decrypt(outfile)and the method-leveldecrypt()keep their signatures.Tests
pytest→ 29 passed.Memory
Measured with
/usr/bin/time -lon a 1.79 GB agile-encrypted.ppsx:The remaining ~0.88 GiB comes from
olefile.OleFileIO.__init__parsing the OLE structure (loadfatbuilding the FAT witharray + array), which is fixed separately in decalage2/olefile#176.