2013-10-01 11:54:09 +13:00
import os
import base64
import json
import hashlib
2014-02-14 16:36:57 +13:00
from Crypto import Random
from Crypto . Cipher import AES
from Crypto . Protocol . KDF import PBKDF2
from util import hook
2013-10-01 14:57:02 +13:00
# helper functions to pad and unpad a string to a specified block size
2013-11-12 07:06:06 +01:00
# <http://stackoverflow.com/questions/12524994/encrypt-decrypt-using-pycrypto-aes-256>
2013-10-01 14:57:02 +13:00
BS = AES . block_size
2013-11-12 07:06:06 +01:00
pad = lambda s : s + ( BS - len ( s ) % BS ) * chr ( BS - len ( s ) % BS )
unpad = lambda s : s [ 0 : - ord ( s [ - 1 ] ) ]
2013-10-01 11:54:09 +13:00
2013-10-01 14:57:02 +13:00
# helper functions to encrypt and encode a string with AES and base64
encode_aes = lambda c , s : base64 . b64encode ( c . encrypt ( pad ( s ) ) )
decode_aes = lambda c , s : unpad ( c . decrypt ( base64 . b64decode ( s ) ) )
2013-10-01 12:17:08 +13:00
db_ready = False
2013-10-01 14:57:02 +13:00
2013-10-01 12:17:08 +13:00
def db_init ( db ) :
2013-10-01 14:57:02 +13:00
""" check to see that our db has the the encryption table. """
2014-02-14 19:43:36 +13:00
global db_ready
if not db_ready :
db . execute ( " create table if not exists encryption(encrypted, iv, "
" primary key(encrypted)) " )
db . commit ( )
db_ready = True
2013-10-01 12:17:08 +13:00
2013-10-01 11:54:09 +13:00
def get_salt ( bot ) :
2013-10-01 14:57:02 +13:00
""" generate an encryption salt if none exists, then returns the salt """
2013-10-01 13:38:00 +13:00
if not bot . config . get ( " random_salt " , False ) :
bot . config [ " random_salt " ] = hashlib . md5 ( os . urandom ( 16 ) ) . hexdigest ( )
json . dump ( bot . config , open ( ' config ' , ' w ' ) , sort_keys = True , indent = 2 )
return bot . config [ " random_salt " ]
2013-10-01 11:54:09 +13:00
@hook.command
2013-10-01 13:18:41 +13:00
def encrypt ( inp , bot = None , db = None , notice = None ) :
2013-10-01 13:38:00 +13:00
""" encrypt <pass> <string> -- Encrypts <string> with <pass>. (<string> can only be decrypted using this bot) """
2014-02-14 19:43:36 +13:00
db_init ( db )
2013-10-01 12:17:08 +13:00
2013-10-01 13:18:41 +13:00
split = inp . split ( " " )
2013-10-01 13:38:00 +13:00
# if there is only one argument, return the help message
2013-10-01 13:18:41 +13:00
if len ( split ) == 1 :
2013-10-01 13:38:00 +13:00
notice ( encrypt . __doc__ )
2013-11-12 07:06:06 +01:00
return
2013-10-01 13:18:41 +13:00
2013-10-01 13:38:00 +13:00
# generate the key from the password and salt
2013-10-01 13:18:41 +13:00
password = split [ 0 ]
2013-10-01 11:54:09 +13:00
salt = get_salt ( bot )
key = PBKDF2 ( password , salt )
2013-10-01 13:38:00 +13:00
# generate the IV and encode it to store in the database
2013-11-12 07:06:06 +01:00
iv = Random . new ( ) . read ( AES . block_size )
2013-10-01 12:17:08 +13:00
iv_encoded = base64 . b64encode ( iv )
2013-10-01 13:38:00 +13:00
# create the AES cipher and encrypt/encode the text with it
2013-10-01 13:18:41 +13:00
text = " " . join ( split [ 1 : ] )
2013-10-01 12:17:08 +13:00
cipher = AES . new ( key , AES . MODE_CBC , iv )
2013-10-01 14:57:02 +13:00
encoded = encode_aes ( cipher , text )
2013-10-01 12:17:08 +13:00
2013-10-01 13:38:00 +13:00
# store the encoded text and IV in the DB for decoding later
2013-10-01 12:17:08 +13:00
db . execute ( " insert or replace into encryption(encrypted, iv) "
" values(?,?) " , ( encoded , iv_encoded ) )
db . commit ( )
return encoded
2013-10-01 11:54:09 +13:00
@hook.command
2013-10-01 13:18:41 +13:00
def decrypt ( inp , bot = None , db = None , notice = None ) :
2013-10-01 13:38:00 +13:00
""" decrypt <pass> <string> -- Decrypts <string> with <pass>. (can only decrypt strings encrypted on this bot) """
2013-10-01 13:18:41 +13:00
if not db_ready :
2013-10-01 13:38:00 +13:00
db_init ( db )
2013-10-01 13:18:41 +13:00
split = inp . split ( " " )
2013-10-01 13:38:00 +13:00
# if there is only one argument, return the help message
2013-10-01 13:18:41 +13:00
if len ( split ) == 1 :
2013-10-01 13:38:00 +13:00
notice ( decrypt . __doc__ )
2013-11-12 07:06:06 +01:00
return
2013-10-01 12:17:08 +13:00
2013-11-12 07:06:06 +01:00
# generate the key from the password and salt
2013-10-01 13:18:41 +13:00
password = split [ 0 ]
2013-10-01 11:54:09 +13:00
salt = get_salt ( bot )
key = PBKDF2 ( password , salt )
2013-10-01 13:18:41 +13:00
text = " " . join ( split [ 1 : ] )
2013-10-01 12:17:08 +13:00
2013-10-01 13:38:00 +13:00
# get the encoded IV from the database and decode it
2013-10-01 12:17:08 +13:00
iv_encoded = db . execute ( " select iv from encryption where "
2014-02-14 17:03:08 +13:00
" encrypted=? " , ( text , ) ) . fetchone ( ) [ 0 ]
2013-10-01 12:17:08 +13:00
iv = base64 . b64decode ( iv_encoded )
2013-10-01 13:38:00 +13:00
# create AES cipher, decode text, decrypt text, and unpad it
2013-10-01 12:17:08 +13:00
cipher = AES . new ( key , AES . MODE_CBC , iv )
2013-11-12 07:06:06 +01:00
return decode_aes ( cipher , text )