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,6 +88,7 @@ import struct
88 import os 88 import os
89 from os.path import splitext, isfile 89 from os.path import splitext, isfile
90 from tempfile import mkstemp 90 from tempfile import mkstemp
  91 +import zipfile
91 from oletools.common.errors import CryptoErrorBase, WrongEncryptionPassword, \ 92 from oletools.common.errors import CryptoErrorBase, WrongEncryptionPassword, \
92 UnsupportedEncryptionError, MaxCryptoNestingReached 93 UnsupportedEncryptionError, MaxCryptoNestingReached
93 from olefile import OleFileIO 94 from olefile import OleFileIO
@@ -103,7 +104,7 @@ except ImportError: @@ -103,7 +104,7 @@ except ImportError:
103 MAX_NESTING_DEPTH = 10 104 MAX_NESTING_DEPTH = 10
104 105
105 106
106 -def is_encrypted(olefile): 107 +def is_encrypted(some_file):
107 """ 108 """
108 Determine whether document contains encrypted content. 109 Determine whether document contains encrypted content.
109 110
@@ -127,15 +128,33 @@ def is_encrypted(olefile): @@ -127,15 +128,33 @@ def is_encrypted(olefile):
127 "well-known property" PIDSI_DOC_SECURITY if the SummaryInformation stream 128 "well-known property" PIDSI_DOC_SECURITY if the SummaryInformation stream
128 is accessible (c.f. [MS-OLEPS] 2.25.1) 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 :returns: True if (and only if) the file contains encrypted content 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 # check well known property for password protection 158 # check well known property for password protection
140 # (this field may be missing for Powerpoint2000, for example) 159 # (this field may be missing for Powerpoint2000, for example)
141 # TODO: check whether password protection always implies encryption. Could 160 # TODO: check whether password protection always implies encryption. Could