Commit 651d81b4970892e355c39cdaa9da50d0c17cc728
1 parent
b98603ac
crypto: extend is_encrypted to zip-based files
Showing
1 changed file
with
26 additions
and
7 deletions
oletools/crypto.py
| ... | ... | @@ -88,6 +88,7 @@ import struct |
| 88 | 88 | import os |
| 89 | 89 | from os.path import splitext, isfile |
| 90 | 90 | from tempfile import mkstemp |
| 91 | +import zipfile | |
| 91 | 92 | from oletools.common.errors import CryptoErrorBase, WrongEncryptionPassword, \ |
| 92 | 93 | UnsupportedEncryptionError, MaxCryptoNestingReached |
| 93 | 94 | from olefile import OleFileIO |
| ... | ... | @@ -103,7 +104,7 @@ except ImportError: |
| 103 | 104 | MAX_NESTING_DEPTH = 10 |
| 104 | 105 | |
| 105 | 106 | |
| 106 | -def is_encrypted(olefile): | |
| 107 | +def is_encrypted(some_file): | |
| 107 | 108 | """ |
| 108 | 109 | Determine whether document contains encrypted content. |
| 109 | 110 | |
| ... | ... | @@ -127,15 +128,33 @@ def is_encrypted(olefile): |
| 127 | 128 | "well-known property" PIDSI_DOC_SECURITY if the SummaryInformation stream |
| 128 | 129 | is accessible (c.f. [MS-OLEPS] 2.25.1) |
| 129 | 130 | |
| 130 | - :param olefile: An opened OleFileIO or a filename to such a file | |
| 131 | - :type olefile: :py:class:`olefile.OleFileIO` or `str` | |
| 131 | + :param some_file: File name or an opened OleFileIO | |
| 132 | + :type some_file: :py:class:`olefile.OleFileIO` or `str` | |
| 132 | 133 | :returns: True if (and only if) the file contains encrypted content |
| 133 | 134 | """ |
| 134 | - if isinstance(olefile, str): | |
| 135 | - ole = olefile.OleFileIO(olefile) | |
| 136 | - else: | |
| 137 | - ole = olefile # assume it is an olefile.OleFileIO | |
| 135 | + if not isinstance(some_file, str): | |
| 136 | + return is_encrypted_ole(some_file) # assume it is OleFileIO | |
| 137 | + if zipfile.is_zipfile(some_file): | |
| 138 | + return is_encrypted_zip(some_file) | |
| 139 | + # otherwise assume it is the name of an ole file | |
| 140 | + return is_encrypted_ole(OleFileIO(some_file)) | |
| 141 | + | |
| 142 | + | |
| 143 | +def is_encrypted_zip(filename): | |
| 144 | + """Specialization of :py:func:`is_encrypted` for zip-based files.""" | |
| 145 | + # try to decrypt a few bytes from first entry | |
| 146 | + with zipfile.ZipFile(filename, 'r') as zipper: | |
| 147 | + first_entry = zipper.infolist()[0] | |
| 148 | + try: | |
| 149 | + with zipper.open(first_entry, 'r') as reader: | |
| 150 | + reader.read(min(16, first_entry.file_size)) | |
| 151 | + return False | |
| 152 | + except RuntimeError as rt_err: | |
| 153 | + return 'crypt' in str(rt_err) | |
| 154 | + | |
| 138 | 155 | |
| 156 | +def is_encrypted_ole(ole): | |
| 157 | + """Specialization of :py:func:`is_encrypted` for ole files.""" | |
| 139 | 158 | # check well known property for password protection |
| 140 | 159 | # (this field may be missing for Powerpoint2000, for example) |
| 141 | 160 | # TODO: check whether password protection always implies encryption. Could | ... | ... |