Commit 651d81b4970892e355c39cdaa9da50d0c17cc728

Authored by Christian Herdtweck
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
... ...