Symmetric Encryption in Python

Published:

Last updated:

Estimated reading time: 2 minutes

In the middle of a project I’m presently working on, I needed to make use of a Symmetric encryption based on the workflow of my software. A key is required by users to encrypt data and the same key will be needed to decrypt data. My first thoughts were on PyCrypto so I went ahead doing this

from Crypto.Cipher import AES
from Crypto import Random
text = 'The quick brown fox jumped over the dog'
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CFB, iv)
ctext = iv + cipher.encrypt(text)

This was totally fine but when I tried decrypting the ctext data, I got something entirely different from what my input was.

decrypted = cipher.decrypt(ctext)

Strange behaviour from MODE_CFB made me try MODE_CBC. Just the exact same way, I went ahead to just change to MODE_CBC

from Crypto.Cipher import AES
from Crypto import Random
text = 'The quick brown fox jumped over the dog'
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
ctext = iv + cipher.encrypt(text)

This mode just fails right away with a warning that the message must be a multiple of 16. I expect any length of message from the users of my program and this didn’t seem like a good idea. While searching the web, I came across a solution that was nice but not what I wanted

http://www.codekoala.com/posts/aes-encryption-python-using-pycrypto/

I wrote my own little function for that

def AEScrypt(action, string):
"""
Solution by codekoala (http://www.codekoala.com/posts/aes-encryption-python-using-pycrypto/)
Requires base64, os, and AES in Crypto.Cipher
@param action string :encrypt or decrypt
@param string string :cipher or plain text
"""

BLOCK_SIZE = 32
PADDING = '{'
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
secret = os.urandom(BLOCK_SIZE)
cipher = AES.new(secret)
result = EncodeAES(cipher, string) if action == 'encrypt' else DecodeAES(cipher, string)
return result

This seem like the best way to use PyCrypto AES encryption but it wasn’t symmetric. It’s not in a PKI format (no key is required to unlock the message).

I still keep that function but it was of no use in my case. Finally I thought of the gnupg module and installed it for my python2

sudo apt-get install python-gnupg

I was able to encrypt this way:

import gnupg,base64
gpg = gnupg.GPG()
def encrypt(passphrase, message):
cipher = gpg.encrypt(message, recipients=None, symmetric='AE256', passphrase=passphrase, armor=True)
return base64.b64encode( str(cipher) )

I had to encode with base64 because I don’t want my encrypted data to have the regular start and end of a GPG encrypted data i.e

----- BEGIN PGP KEY -----

----- END PGP KEY -------

To decrypt the encrypted data, I use this:

def decrypt(cipher, passphrase):
deciphered = str( gpg.decrypt( base64.b64decode(cipher), passphrase ) )
return deciphered if deciphered is True else 'Incorrect passphrase'

When a wrong passphrase is used gpg.decrypt() returns an empty string ` ` which is False. If it is true we get our deciphered text.

cryptographyencryptionpython

Newer post

Using source maps

Older post

Appreciating developers

Responses

You may respond to this post by referencing it on your blog, twitter, or any website