Given the popularity of Python, at first I was disappointed that there was no complete answer to this question to be found. It took me a fair amount of reading different answers, as well as other resources, to get it right. I thought I might share the result for future reference and perhaps review; I’m by no means a cryptography expert! However, the code below appears to work good:

<code><span class="kwd">from</span><span class="pln"> hashlib </span><span class="kwd">import</span><span class="pln"> md5
</span><span class="kwd">from</span> <span class="typ">Crypto</span><span class="pun">.</span><span class="typ">Cipher</span> <span class="kwd">import</span><span class="pln"> AES
</span><span class="kwd">from</span> <span class="typ">Crypto</span> <span class="kwd">import</span> <span class="typ">Random</span>

<span class="kwd">def</span><span class="pln"> derive_key_and_iv</span><span class="pun">(</span><span class="pln">password</span><span class="pun">,</span><span class="pln"> salt</span><span class="pun">,</span><span class="pln"> key_length</span><span class="pun">,</span><span class="pln"> iv_length</span><span class="pun">):</span><span class="pln">
    d </span><span class="pun">=</span><span class="pln"> d_i </span><span class="pun">=</span> <span class="str">''</span>
    <span class="kwd">while</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">d</span><span class="pun">)</span> <span class="pun">&lt;</span><span class="pln"> key_length </span><span class="pun">+</span><span class="pln"> iv_length</span><span class="pun">:</span><span class="pln">
        d_i </span><span class="pun">=</span><span class="pln"> md5</span><span class="pun">(</span><span class="pln">d_i </span><span class="pun">+</span><span class="pln"> password </span><span class="pun">+</span><span class="pln"> salt</span><span class="pun">).</span><span class="pln">digest</span><span class="pun">()</span><span class="pln">
        d </span><span class="pun">+=</span><span class="pln"> d_i
    </span><span class="kwd">return</span><span class="pln"> d</span><span class="pun">[:</span><span class="pln">key_length</span><span class="pun">],</span><span class="pln"> d</span><span class="pun">[</span><span class="pln">key_length</span><span class="pun">:</span><span class="pln">key_length</span><span class="pun">+</span><span class="pln">iv_length</span><span class="pun">]</span>

<span class="kwd">def</span><span class="pln"> encrypt</span><span class="pun">(</span><span class="pln">in_file</span><span class="pun">,</span><span class="pln"> out_file</span><span class="pun">,</span><span class="pln"> password</span><span class="pun">,</span><span class="pln"> key_length</span><span class="pun">=</span><span class="lit">32</span><span class="pun">):</span><span class="pln">
    bs </span><span class="pun">=</span><span class="pln"> AES</span><span class="pun">.</span><span class="pln">block_size
    salt </span><span class="pun">=</span> <span class="typ">Random</span><span class="pun">.</span><span class="pln">new</span><span class="pun">().</span><span class="pln">read</span><span class="pun">(</span><span class="pln">bs </span><span class="pun">-</span><span class="pln"> len</span><span class="pun">(</span><span class="str">'Salted__'</span><span class="pun">))</span><span class="pln">
    key</span><span class="pun">,</span><span class="pln"> iv </span><span class="pun">=</span><span class="pln"> derive_key_and_iv</span><span class="pun">(</span><span class="pln">password</span><span class="pun">,</span><span class="pln"> salt</span><span class="pun">,</span><span class="pln"> key_length</span><span class="pun">,</span><span class="pln"> bs</span><span class="pun">)</span><span class="pln">
    cipher </span><span class="pun">=</span><span class="pln"> AES</span><span class="pun">.</span><span class="pln">new</span><span class="pun">(</span><span class="pln">key</span><span class="pun">,</span><span class="pln"> AES</span><span class="pun">.</span><span class="pln">MODE_CBC</span><span class="pun">,</span><span class="pln"> iv</span><span class="pun">)</span><span class="pln">
    out_file</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="str">'Salted__'</span> <span class="pun">+</span><span class="pln"> salt</span><span class="pun">)</span><span class="pln">
    finished </span><span class="pun">=</span> <span class="kwd">False</span>
    <span class="kwd">while</span> <span class="kwd">not</span><span class="pln"> finished</span><span class="pun">:</span><span class="pln">
        chunk </span><span class="pun">=</span><span class="pln"> in_file</span><span class="pun">.</span><span class="pln">read</span><span class="pun">(</span><span class="lit">1024</span> <span class="pun">*</span><span class="pln"> bs</span><span class="pun">)</span>
        <span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span> <span class="pun">==</span> <span class="lit">0</span> <span class="kwd">or</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span> <span class="pun">%</span><span class="pln"> bs </span><span class="pun">!=</span> <span class="lit">0</span><span class="pun">:</span><span class="pln">
            padding_length </span><span class="pun">=</span> <span class="pun">(</span><span class="pln">bs </span><span class="pun">-</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span> <span class="pun">%</span><span class="pln"> bs</span><span class="pun">)</span> <span class="kwd">or</span><span class="pln"> bs
            chunk </span><span class="pun">+=</span><span class="pln"> padding_length </span><span class="pun">*</span><span class="pln"> chr</span><span class="pun">(</span><span class="pln">padding_length</span><span class="pun">)</span><span class="pln">
            finished </span><span class="pun">=</span> <span class="kwd">True</span><span class="pln">
        out_file</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">cipher</span><span class="pun">.</span><span class="pln">encrypt</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">))</span>

<span class="kwd">def</span><span class="pln"> decrypt</span><span class="pun">(</span><span class="pln">in_file</span><span class="pun">,</span><span class="pln"> out_file</span><span class="pun">,</span><span class="pln"> password</span><span class="pun">,</span><span class="pln"> key_length</span><span class="pun">=</span><span class="lit">32</span><span class="pun">):</span><span class="pln">
    bs </span><span class="pun">=</span><span class="pln"> AES</span><span class="pun">.</span><span class="pln">block_size
    salt </span><span class="pun">=</span><span class="pln"> in_file</span><span class="pun">.</span><span class="pln">read</span><span class="pun">(</span><span class="pln">bs</span><span class="pun">)[</span><span class="pln">len</span><span class="pun">(</span><span class="str">'Salted__'</span><span class="pun">):]</span><span class="pln">
    key</span><span class="pun">,</span><span class="pln"> iv </span><span class="pun">=</span><span class="pln"> derive_key_and_iv</span><span class="pun">(</span><span class="pln">password</span><span class="pun">,</span><span class="pln"> salt</span><span class="pun">,</span><span class="pln"> key_length</span><span class="pun">,</span><span class="pln"> bs</span><span class="pun">)</span><span class="pln">
    cipher </span><span class="pun">=</span><span class="pln"> AES</span><span class="pun">.</span><span class="pln">new</span><span class="pun">(</span><span class="pln">key</span><span class="pun">,</span><span class="pln"> AES</span><span class="pun">.</span><span class="pln">MODE_CBC</span><span class="pun">,</span><span class="pln"> iv</span><span class="pun">)</span><span class="pln">
    next_chunk </span><span class="pun">=</span> <span class="str">''</span><span class="pln">
    finished </span><span class="pun">=</span> <span class="kwd">False</span>
    <span class="kwd">while</span> <span class="kwd">not</span><span class="pln"> finished</span><span class="pun">:</span><span class="pln">
        chunk</span><span class="pun">,</span><span class="pln"> next_chunk </span><span class="pun">=</span><span class="pln"> next_chunk</span><span class="pun">,</span><span class="pln"> cipher</span><span class="pun">.</span><span class="pln">decrypt</span><span class="pun">(</span><span class="pln">in_file</span><span class="pun">.</span><span class="pln">read</span><span class="pun">(</span><span class="lit">1024</span> <span class="pun">*</span><span class="pln"> bs</span><span class="pun">))</span>
        <span class="kwd">if</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">next_chunk</span><span class="pun">)</span> <span class="pun">==</span> <span class="lit">0</span><span class="pun">:</span><span class="pln">
            padding_length </span><span class="pun">=</span><span class="pln"> ord</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">[-</span><span class="lit">1</span><span class="pun">])</span><span class="pln">
            chunk </span><span class="pun">=</span><span class="pln"> chunk</span><span class="pun">[:-</span><span class="pln">padding_length</span><span class="pun">]</span><span class="pln">
            finished </span><span class="pun">=</span> <span class="kwd">True</span><span class="pln">
        out_file</span><span class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span class="pln">chunk</span><span class="pun">)</span></code>

Usage:

<code><span class="kwd">with</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">in_filename</span><span class="pun">,</span> <span class="str">'rb'</span><span class="pun">)</span> <span class="kwd">as</span><span class="pln"> in_file</span><span class="pun">,</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">out_filename</span><span class="pun">,</span> <span class="str">'wb'</span><span class="pun">)</span> <span class="kwd">as</span><span class="pln"> out_file</span><span class="pun">:</span><span class="pln">
    encrypt</span><span class="pun">(</span><span class="pln">in_file</span><span class="pun">,</span><span class="pln"> out_file</span><span class="pun">,</span><span class="pln"> password</span><span class="pun">)</span>
<span class="kwd">with</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">in_filename</span><span class="pun">,</span> <span class="str">'rb'</span><span class="pun">)</span> <span class="kwd">as</span><span class="pln"> in_file</span><span class="pun">,</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">out_filename</span><span class="pun">,</span> <span class="str">'wb'</span><span class="pun">)</span> <span class="kwd">as</span><span class="pln"> out_file</span><span class="pun">:</span><span class="pln">
    decrypt</span><span class="pun">(</span><span class="pln">in_file</span><span class="pun">,</span><span class="pln"> out_file</span><span class="pun">,</span><span class="pln"> password</span><span class="pun">)</span></code>

If you see a chance to improve on this code or extend it to be more flexible (e.g. make it work without salt, or provide Python 3 compatibility), please feel free to do so.

Take your time to comment on this article.

Leave a Reply