Commit 1f04b96d5eb601fa84e97274d84136d78d6ab0e2
1 parent
826bee0b
crypto: layout how to best integrate crypto into code
Showing
1 changed file
with
37 additions
and
0 deletions
oletools/crypto.py
| @@ -8,6 +8,43 @@ information on encryption in OLE files. | @@ -8,6 +8,43 @@ information on encryption in OLE files. | ||
| 8 | Uses :py:mod:`msoffcrypto-tool` to decrypt if it is available. Otherwise | 8 | Uses :py:mod:`msoffcrypto-tool` to decrypt if it is available. Otherwise |
| 9 | decryption will fail with an ImportError. | 9 | decryption will fail with an ImportError. |
| 10 | 10 | ||
| 11 | +Encryption/Write-Protection can be realized in many different ways. They range | ||
| 12 | +from setting a single flag in an otherwise unprotected file to embedding a | ||
| 13 | +regular file (e.g. xlsx) in an EncryptedStream inside an OLE file. That means | ||
| 14 | +that (1) that lots of bad things are accesible even if no encryption password | ||
| 15 | +is known, and (2) even basic attributes like the file type can change by | ||
| 16 | +decryption. Therefore I suggest the following general routine to deal with | ||
| 17 | +potentially encrypted files:: | ||
| 18 | + | ||
| 19 | + def script_main_function(input_file, args): | ||
| 20 | + '''Wrapper around main function to deal with encrypted files.''' | ||
| 21 | + initial_stuff(input_file, args) | ||
| 22 | + result = None | ||
| 23 | + try: | ||
| 24 | + result = do_your_thing_assuming_no_encryption(input_file) | ||
| 25 | + if not crypto_is_encrypted(input_file): | ||
| 26 | + return result | ||
| 27 | + except Exception: | ||
| 28 | + if not crypto_is_encrypted(input_file): | ||
| 29 | + raise | ||
| 30 | + decrypted_file = None | ||
| 31 | + try: | ||
| 32 | + decrypted_file = crypto.decrypt(input_file) | ||
| 33 | + except Exception: | ||
| 34 | + raise | ||
| 35 | + finally: # clean up | ||
| 36 | + try: # (maybe file was not yet created) | ||
| 37 | + os.unlink(decrypted_file) | ||
| 38 | + except Exception: | ||
| 39 | + pass | ||
| 40 | + | ||
| 41 | +That means that caller code needs another wrapper around its main function. I | ||
| 42 | +did try it another way first (a transparent on-demand unencrypt) but for the | ||
| 43 | +above reasons I believe this is the better way. Also, non-top-level-code can | ||
| 44 | +just assume that it works on unencrypted data and fail with an exception if | ||
| 45 | +encrypted data makes its work impossible. No need to check `if is_encrypted()` | ||
| 46 | +at the start of functions. | ||
| 47 | + | ||
| 11 | .. seealso:: [MS-OFFCRYPTO] | 48 | .. seealso:: [MS-OFFCRYPTO] |
| 12 | .. seealso:: https://github.com/nolze/msoffcrypto-tool | 49 | .. seealso:: https://github.com/nolze/msoffcrypto-tool |
| 13 | 50 |