Skip to content

Commit

Permalink
test: encryption method
Browse files Browse the repository at this point in the history
  • Loading branch information
mindcruzer committed Sep 19, 2015
1 parent 37d2d3b commit aafaf57
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 12 deletions.
31 changes: 19 additions & 12 deletions encrypt_content/encrypt_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Encrypt Content
-----------------
A pelican plugin to encrypt content.
A pelican plugin to password protect content.
"""
import os
Expand Down Expand Up @@ -38,19 +38,26 @@
}


def _encrypt_text_aes(text, password):
def hash_md5(text):
"""
Creates an md5 hash from text.
"""
key = hashlib.md5()
key.update(text.encode('utf-8'))
return key.digest()


def encrypt_text_aes(text, password):
"""
Encrypts text with AES-256.
"""
BLOCK_SIZE = 32
PADDING_CHAR = b'^'

iv = Random.new().read(16)

# key must be 32 bytes for AES-256
key = hashlib.md5()
key.update(password.encode('utf-8'))
cipher = AES.new(key.digest(), AES.MODE_CBC, iv)

# key must be 32 bytes for AES-256, so the password is hashed with md5 first
cipher = AES.new(hash_md5(password), AES.MODE_CBC, iv)

plaintext = text.encode('utf-8')

Expand All @@ -65,16 +72,16 @@ def _encrypt_text_aes(text, password):
)


def _encrypt_content(content):
def encrypt_content(content):
"""
Replaces page or article content with decrypt form.
"""
ciphertext_bundle = _encrypt_text_aes(content._content, content.password)
ciphertext_bundle = encrypt_text_aes(content._content, content.password)

decrypt_form = Template(DECRYPT_FORM_TPL).render({
'summary': settings['summary'],
# this benign decoding is necessary before writing to the template,
# otherwise our output string will be wrapped with b''
# otherwise the output string will be wrapped with b''
'ciphertext_bundle': b';'.join(ciphertext_bundle).decode('ascii'),
'js_libraries': JS_LIBRARIES
})
Expand Down Expand Up @@ -102,11 +109,11 @@ def pelican_all_generators_finalized(content_generators):
if isinstance(generator, generators.ArticlesGenerator):
for article in generator.articles + generator.translations:
if hasattr(article, 'password'):
_encrypt_content(article)
encrypt_content(article)
if isinstance(generator, generators.PagesGenerator):
for page in generator.pages:
if 'password' in page.metadata:
_encrypt_content(page)
encrypt_content(page)


def register():
Expand Down
40 changes: 40 additions & 0 deletions encrypt_content/test_encrypt_content.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import unittest
import base64

from Crypto.Cipher import AES

from encrypt_content import (
hash_md5,
encrypt_text_aes
)


class TestEncryptContent(unittest.TestCase):

def test_hash_md5(self):
"""
Verify that md5 hashes are being computed correctly.
"""
md5_bytes = hash_md5('test')
self.assertEqual(md5_bytes.encode('hex'), '098f6bcd4621d373cade4e832627b4f6')

def test_encrypt_text_aes(self):
"""
Verify that the ciphertext is actually decryptable.
Note that just because this passes doesn't mean everything is necessarily
working, as in production the actual decryption is done in JavaScript.
"""
text = 'some text to encrypt'
password = 'agoodpassword'

iv_b64, ciphertext_b64, padding_char = encrypt_text_aes(text, password)

cipher = AES.new(hash_md5(password), AES.MODE_CBC, base64.b64decode(iv_b64))
plaintext = cipher.decrypt(base64.b64decode(ciphertext_b64))

self.assertEqual(plaintext.decode('utf-8').rstrip(padding_char), text)


if __name__ == "__main__":
unittest.main()

0 comments on commit aafaf57

Please sign in to comment.