
    Zi+B                     p   S r SrSSKrSSKrSSKrSSKJrJr  SSKJ	r	  SSK
r
SSKJr   SSKr\R                  R!                  \R                  R#                  \R                  R%                  \5      5      5      r\R                  R!                  \R                  R+                  \S5      5      r\\R                  ;  a  \R                  R/                  S\5        SSKJrJrJrJrJr  SS	KJr  S
r \RB                  " S5      r"S r#S r$S r%S r&Sr'Sr(\'\(SSSSS/r)S r*S r+SS jr,g! \ a    Sr GNf = f)a
  
crypto.py

Module to be used by other scripts and modules in oletools, that provides
information on encryption in OLE files.

Uses :py:mod:`msoffcrypto-tool` to decrypt if it is available. Otherwise
decryption will fail with an ImportError.

Encryption/Write-Protection can be realized in many different ways. They range
from setting a single flag in an otherwise unprotected file to embedding a
regular file (e.g.  xlsx) in an EncryptedStream inside an OLE file. That means
that (1) that lots of bad things are accesible even if no encryption password
is known, and (2) even basic attributes like the file type can change by
decryption. Therefore I suggest the following general routine to deal with
potentially encrypted files::

    def script_main_function(input_file, passwords, crypto_nesting=0, args):
        '''Wrapper around main function to deal with encrypted files.'''
        initial_stuff(input_file, args)
        result = None
        try:
            result = do_your_thing_assuming_no_encryption(input_file)
            if not crypto.is_encrypted(input_file):
                return result
        except Exception:
            if not crypto.is_encrypted(input_file):
                raise
        # we reach this point only if file is encrypted
        # check if this is an encrypted file in an encrypted file in an ...
        if crypto_nesting >= crypto.MAX_NESTING_DEPTH:
            raise crypto.MaxCryptoNestingReached(crypto_nesting, filename)
        decrypted_file = None
        try:
            decrypted_file = crypto.decrypt(input_file, passwords)
            if decrypted_file is None:
                raise crypto.WrongEncryptionPassword(input_file)
            # might still be encrypted, so call this again recursively
            result = script_main_function(decrypted_file, passwords,
                                          crypto_nesting+1, args)
        except Exception:
            raise
        finally:     # clean up
            try:     # (maybe file was not yet created)
                os.unlink(decrypted_file)
            except Exception:
                pass

(Realized e.g. in :py:mod:`oletools.msodde`).
That means that caller code needs another wrapper around its main function. I
did try it another way first (a transparent on-demand unencrypt) but for the
above reasons I believe this is the better way. Also, non-top-level-code can
just assume that it works on unencrypted data and fail with an exception if
encrypted data makes its work impossible. No need to check `if is_encrypted()`
at the start of functions.

.. seealso:: [MS-OFFCRYPTO]
.. seealso:: https://github.com/nolze/msoffcrypto-tool

crypto is part of the python-oletools package:
http://www.decalage.info/python/oletools
z0.60    N)splitextisfile)mkstemp)	OleFileIOz..)CryptoErrorBaseWrongEncryptionPasswordUnsupportedEncryptionErrorMaxCryptoNestingReachedCryptoLibNotImported)
log_helper
   cryptoc                  J    [         R                  [        R                  5        g)z
Enable logging for this module (disabled by default).

For use by third-party libraries that import `crypto` as module.

This will set the module-specific logger level to NOTSET, which
means the main application controls the actual logging level.
N)logsetLevelr   NOTSET     i/var/www/eduai.edurigo.com/question_generate/ques_gen_env/lib/python3.13/site-packages/oletools/crypto.pyenable_loggingr      s     LL""#r   c                 n   [        5       (       a  [        R                  S5        SnSn [        U [        5      (       a.  U R
                  nUR                  5       nUR                  S5        O[        U S5      n[        R                  " U5      R                  5        Ub  UR                  U5        $ UR                  5         $  [        U [        5      (       a  [#        U 5      $ [$        R&                  " U 5      (       a  [)        U 5      $ [	        U 5       n[#        U5      sSSS5        $ ! [         a.  n[        R                  SR                  U5      5         SnA$ SnAff = f! [         a.  n[        R!                  SR                  U5      5         SnAOSnAff = f  Ub  UR                  U5        NUR                  5         GN! [         a/  n[        R                  SR                  U5      5         SnAGN:SnAff = f!  Ub  UR                  U5        f UR                  5         f ! [         a.  n[        R                  SR                  U5      5         SnAf SnAff = f= f! , (       d  f       g= f! [         a.  n[        R!                  SR                  X5      5         SnAgSnAff = f)	a  
Determine whether document contains encrypted content.

This should return False for documents that are just write-protected or
signed or finalized. It should return True if ANY content of the file is
encrypted and can therefore not be analyzed by other oletools modules
without given a password.

Exception: there are way to write-protect an office document by embedding
it as encrypted stream with hard-coded standard password into an otherwise
empty OLE file. From an office user point of view, this is no encryption,
but regarding file structure this is encryption, so we return `True` for
these.

This should not raise exceptions needlessly.

This implementation is rather simple: it returns True if the file contains
streams with typical encryption names (c.f. [MS-OFFCRYPTO]). It does not
test whether these streams actually contain data or whether the ole file
structure contains the necessary references to these. It also checks the
"well-known property" PIDSI_DOC_SECURITY if the SummaryInformation stream
is accessible (c.f. [MS-OLEPS] 2.25.1)

:param some_file: File name or an opened OleFileIO
:type some_file: :py:class:`olefile.OleFileIO` or `str`
:returns: True if (and only if) the file contains encrypted content
z)Checking for encryption using msoffcryptoNr   rbz"Ignoring error during clean up: {}zImsoffcrypto failed to parse file or determine whether it is encrypted: {}zCFailed to check {} for encryption ({}); assume it is not encrypted.F)check_msoffcryptor   debug
isinstancer   fptellseekopenmsoffcrypto
OfficeFileis_encryptedclose	Exceptionwarningformatinfo_is_encrypted_olezipfile
is_zipfile_is_encrypted_zip)	some_filefile_handlefile_posexcoles        r   r"   r"      sF   : 		=>	N)Y//'ll&++-  #"9d3))+6CCEN'$$X.%%'
9i++$Y//i(($Y//y!S$S) "!  N@GGLMMN  	&HH 6& &	&&N'$$X.%%' N@GGLMMNN'$$X.%%' N@GGLMMN "!   9 !!'	!7	9 	9 9s   A2E% D*2D*I< $%I< 
I< I+ 	I< *
E"4$EE"%
F/$FH FH "G	 7G	 	
H$G==HI(H-I(H-,I(-
I%7$I I( I%%I(+
I95I< 9I< <
J4$J//J4c                    [         R                  S5        [        R                  " U S5       nUR	                  5       S   n UR                  US5       nUR                  [        SUR                  5      5        SSS5         SSS5        g! , (       d  f       N= f! [         a!  nS[        U5      ;   s SnAsSSS5        $ SnAff = f! , (       d  f       g= f)z>Specialization of :py:func:`is_encrypted` for zip-based files.z#Checking for encryption in zip filerr      NFcrypt)r   r   r)   ZipFileinfolistr   readmin	file_sizeRuntimeErrorstr)filenamezipperfirst_entryreaderrt_errs        r   r+   r+      s    II34 
3	'6oo'*	*[#.&CK$9$9:; / 
(	' /.  	*c&k)) 
(	'	* 
(	'sM   CB&B:B
B	B
C'C4C5CCC
Cc                    [         R                  S5        U R                  S5      (       a%  U R                  S5      nSU;   a  US   S-  (       a  gU R                  S5      (       a  [         R                  S5        gU R                  S5      (       a  U R                  S	5      (       d  gU R                  S
5      (       a  Sn U R	                  S
/5      nUR                  S5        [        R                  " SUR                  S5      5      S   nUS-  S-	  nU(       a   Ub  UR                  5         gg Ub  UR                  5         gg! Ub  UR                  5         f f = f)z8Specialization of :py:func:`is_encrypted` for ole files.z#Checking for encryption in OLE filezSummaryInformation      TEncryptionInfozfound stream EncryptionInfoEncryptedSummarySummaryInformationWordDocumentNr   H   r         F)	r   r   existsgetproperties
openstreamr7   structunpackr#   )r0   suminfo_datastreamtemp16f_encrypteds        r   r(   r(      s=   II34
 zz*++(()AB<\$%7!%;
 zz"##		/0
zz$%%

/00 zz.!!	^^^$45FKKO]]3A7:F!F?q0K! "  ! 	 ! "s   AE
 
E VelvetSweatshopz/01Hannes Ruescher/011231234123451234564321c                  &    [         c
  [        5       eg)zERaise a :py:class:`CryptoLibNotImported` if msoffcrypto not imported.N)r    r   r   r   r   _check_msoffcryptor\   2  s    "$$ r   c                      [         SL$ )z:Return `True` iff :py:mod:`msoffcrypto` could be imported.N)r    r   r   r   r   r   8  s    d""r   c                    [        5         [        U[        5      (       a  U4nOU(       d  [        nSU;  a  SUS'   SU;  a  [	        U 5      S   US'   SUS'   Sn[        U S5       n [        R                  " U5      nWR!                  5       (       d  [#        SR%                  U 5      5      eU H  n[        R                  SR%                  U5      5        SnSn	Sn UR'                  US9  [)        S0 UD6u  p[*        R,                  " US5      n	SnUR/                  U	5        U	R1                  5         Sn	  O   SSS5        [        R                  S5        U$ ! [         ac  nS	[        U5      ;   aN  [        R                  S
SS9  [        R                  R                  S:X  a  [        U 5      e[        S5         SnAGNAe SnAff = f! [         a}    [        R                  SSS9  U	(       a  U	R1                  5         OU(       a  [*        R0                  " U5        U(       a&  [3        U5      (       a  [*        R4                  " U5        Sn GM  f = f! , (       d  f       GN = f)a  
Try to decrypt an encrypted file

This function tries to decrypt the given file using a given set of
passwords. If no password is given, tries the standard password for write
protection. Creates a file with decrypted data whose file name is returned.
If the decryption fails, None is returned.

:param str filename: path to an ole file on disc
:param passwords: list/set/tuple/... of passwords or a single password or
                  None
:type passwords: iterable or str or None
:param temp_file_args: arguments for :py:func:`tempfile.mkstemp` e.g.,
                       `dirname` or `prefix`. `suffix` will default to
                       suffix of input `filename`, `prefix` defaults to
                       `oletools-decrypt-`; `text` will be ignored
:returns: name of the decrypted temporary file (type str) or `None`
:raises: :py:class:`ImportError` if :py:mod:`msoffcrypto-tools` not found
:raises: :py:class:`ValueError` if the given file is not encrypted
prefixzoletools-decrypt-suffixrC   FtextNr   zUnrecognized file formatzCaught exceptionT)exc_inforI   z4raise UnsupportedEncryptionError(filename) from Nonez%Given input file {} is not encrypted!z$Trying to decrypt with password {!r})passwordwbzFailed to decryptzAll passwords failedr   )r\   r   r;   DEFAULT_PASSWORDSr   r   r    r!   r$   r   r   sysversion_infomajorr	   execr"   
ValueErrorr&   load_keyr   osfdopendecryptr#   r   unlink)
r<   	passwordstemp_file_argsdecrypt_filer?   crypto_filer/   rc   write_descriptorwrite_handles
             r   rn   rn   =  s3   *  )S!!M	%	 ~%#6x ~%#+H#5a#8x "N6L	h		%008K ''))D$fX.0 0 "HII<CCHMN#LL$$$h$7 291J>1J. !yy)94@#' ##L1 ""$## "' 
b II$%_  	)SX5		,t	< ##))Q.4X>> OPP	D  
$		-	=   &&(%HH-.F<$8$8IIl+#
$K 
	sd   &I(E>AIAG5I
G"AG 9I?G  GIBI
III
I!)N)-__doc____version__rf   rO   rl   os.pathr   r   tempfiler   r)   olefiler   r    ImportErrorpathnormpathabspathdirname__file___thismodule_dirjoin_parent_dirinsertoletools.common.errorsr   r   r	   r
   r   oletools.common.log_helperr   MAX_NESTING_DEPTHget_or_create_silent_loggerr   r   r"   r+   r(   EXCEL_TRANSPARENT_PASSWORDPOWERPOINT_TRANSPARENT_PASSWORDre   r\   r   rn   r   r   r   <module>r      s2  =D  
  	 $    ''""277??277??83L#MNggrww||OTBCchhHHOOA{#N N 1
  
 ,,X6	$K\*)` / 
 #:  01PFGXv? %#
WY  Ks   D) )D54D5