From b38c540bf3225861808054a27cbb0df1a502d55e Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Tue, 1 Oct 2013 11:54:09 +1300 Subject: [PATCH 1/5] Added prototype plugin --- plugins/encrypt.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 plugins/encrypt.py diff --git a/plugins/encrypt.py b/plugins/encrypt.py new file mode 100644 index 0000000..1f23d98 --- /dev/null +++ b/plugins/encrypt.py @@ -0,0 +1,43 @@ +from util import hook +from Crypto.Cipher import AES +from Crypto.Protocol.KDF import PBKDF2 + +import os +import base64 +import json +import hashlib + +BS = 16 +pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) +unpad = lambda s : s[0:-ord(s[-1])] + + +def get_salt(bot): + 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"] + + +@hook.command +def encrypt(inp, bot=None): + """encrypt -- Encrypts with .""" + password = inp.split(" ")[0] + salt = get_salt(bot) + key = PBKDF2(password, salt) + + text = " ".join(inp.split(" ")[1:]) + cipher = AES.new(key, AES.MODE_ECB) # never use ECB in strong systems obviously + return base64.b64encode(cipher.encrypt(pad(text))) + + +@hook.command +def decrypt(inp, bot=None): + """decrypt -- Decrypts with .""" + password = inp.split(" ")[0] + salt = get_salt(bot) + key = PBKDF2(password, salt) + + text = " ".join(inp.split(" ")[1:]) + cipher = AES.new(key, AES.MODE_ECB) # never use ECB in strong systems obviously + return unpad(cipher.decrypt(base64.b64decode(text))) \ No newline at end of file From 2ae2a8575ace1a4511523742c8fdb1c1b5f50e3a Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Tue, 1 Oct 2013 12:17:08 +1300 Subject: [PATCH 2/5] OTT encryption for silly secret messages --- plugins/encrypt.py | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/plugins/encrypt.py b/plugins/encrypt.py index 1f23d98..64b4628 100644 --- a/plugins/encrypt.py +++ b/plugins/encrypt.py @@ -1,4 +1,5 @@ from util import hook +from Crypto import Random from Crypto.Cipher import AES from Crypto.Protocol.KDF import PBKDF2 @@ -11,6 +12,15 @@ BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-ord(s[-1])] +db_ready = False + +def db_init(db): + """check to see that our db has the the encryption table and return a connection.""" + db.execute("create table if not exists encryption(encrypted, iv, " + "primary key(encrypted))") + db.commit() + db_ready = True + def get_salt(bot): if not bot.config.get("random_salt", False): @@ -20,24 +30,43 @@ def get_salt(bot): @hook.command -def encrypt(inp, bot=None): +def encrypt(inp, bot=None, db=None): """encrypt -- Encrypts with .""" + db_init(db) + password = inp.split(" ")[0] salt = get_salt(bot) key = PBKDF2(password, salt) + iv = Random.new().read(AES.block_size); + iv_encoded = base64.b64encode(iv) + text = " ".join(inp.split(" ")[1:]) - cipher = AES.new(key, AES.MODE_ECB) # never use ECB in strong systems obviously - return base64.b64encode(cipher.encrypt(pad(text))) + cipher = AES.new(key, AES.MODE_CBC, iv) + encrypted = cipher.encrypt(pad(text)) + encoded = base64.b64encode(encrypted) + + db.execute("insert or replace into encryption(encrypted, iv)" + "values(?,?)", (encoded, iv_encoded)) + db.commit() + + return encoded @hook.command -def decrypt(inp, bot=None): +def decrypt(inp, bot=None, db=None): """decrypt -- Decrypts with .""" + db_init(db) + password = inp.split(" ")[0] salt = get_salt(bot) key = PBKDF2(password, salt) text = " ".join(inp.split(" ")[1:]) - cipher = AES.new(key, AES.MODE_ECB) # never use ECB in strong systems obviously + + iv_encoded = db.execute("select iv from encryption where" + " encrypted=?", (text,)).fetchone()[0] + iv = base64.b64decode(iv_encoded) + + cipher = AES.new(key, AES.MODE_CBC, iv) return unpad(cipher.decrypt(base64.b64decode(text))) \ No newline at end of file From 72dce244b42673ab33dee26a7f48a677d1acbeca Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Tue, 1 Oct 2013 13:18:41 +1300 Subject: [PATCH 3/5] anti-asshat --- plugins/encrypt.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/plugins/encrypt.py b/plugins/encrypt.py index 64b4628..75da11b 100644 --- a/plugins/encrypt.py +++ b/plugins/encrypt.py @@ -30,18 +30,25 @@ def get_salt(bot): @hook.command -def encrypt(inp, bot=None, db=None): +def encrypt(inp, bot=None, db=None, notice=None): """encrypt -- Encrypts with .""" - db_init(db) + if not db_ready: + db_init(db) - password = inp.split(" ")[0] + split = inp.split(" ") + + if len(split) == 1: + notice(encrypt.__doc__) + return + + password = split[0] salt = get_salt(bot) key = PBKDF2(password, salt) iv = Random.new().read(AES.block_size); iv_encoded = base64.b64encode(iv) - text = " ".join(inp.split(" ")[1:]) + text = " ".join(split[1:]) cipher = AES.new(key, AES.MODE_CBC, iv) encrypted = cipher.encrypt(pad(text)) encoded = base64.b64encode(encrypted) @@ -54,15 +61,22 @@ def encrypt(inp, bot=None, db=None): @hook.command -def decrypt(inp, bot=None, db=None): +def decrypt(inp, bot=None, db=None, notice=None): """decrypt -- Decrypts with .""" - db_init(db) + if not db_ready: + db_init(db) - password = inp.split(" ")[0] + split = inp.split(" ") + + if len(split) == 1: + notice(decrypt.__doc__) + return + + password = split[0] salt = get_salt(bot) key = PBKDF2(password, salt) - text = " ".join(inp.split(" ")[1:]) + text = " ".join(split[1:]) iv_encoded = db.execute("select iv from encryption where" " encrypted=?", (text,)).fetchone()[0] From 46f571382cd8d6c3a7001e0b539c43ef1030aee9 Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Tue, 1 Oct 2013 13:38:00 +1300 Subject: [PATCH 4/5] Comments --- plugins/encrypt.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/plugins/encrypt.py b/plugins/encrypt.py index 75da11b..7618cd5 100644 --- a/plugins/encrypt.py +++ b/plugins/encrypt.py @@ -23,36 +23,41 @@ def db_init(db): def get_salt(bot): - 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"] + 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"] @hook.command def encrypt(inp, bot=None, db=None, notice=None): - """encrypt -- Encrypts with .""" + """encrypt -- Encrypts with . ( can only be decrypted using this bot)""" if not db_ready: - db_init(db) + db_init(db) split = inp.split(" ") + # if there is only one argument, return the help message if len(split) == 1: - notice(encrypt.__doc__) - return + notice(encrypt.__doc__) + return + # generate the key from the password and salt password = split[0] salt = get_salt(bot) key = PBKDF2(password, salt) + # generate the IV and encode it to store in the database iv = Random.new().read(AES.block_size); iv_encoded = base64.b64encode(iv) + # create the AES cipher and encrypt/encode the text with it text = " ".join(split[1:]) cipher = AES.new(key, AES.MODE_CBC, iv) encrypted = cipher.encrypt(pad(text)) encoded = base64.b64encode(encrypted) + # store the encoded text and IV in the DB for decoding later db.execute("insert or replace into encryption(encrypted, iv)" "values(?,?)", (encoded, iv_encoded)) db.commit() @@ -62,25 +67,29 @@ def encrypt(inp, bot=None, db=None, notice=None): @hook.command def decrypt(inp, bot=None, db=None, notice=None): - """decrypt -- Decrypts with .""" + """decrypt -- Decrypts with . (can only decrypt strings encrypted on this bot)""" if not db_ready: - db_init(db) + db_init(db) split = inp.split(" ") + # if there is only one argument, return the help message if len(split) == 1: - notice(decrypt.__doc__) - return + notice(decrypt.__doc__) + return + # generate the key from the password and salt password = split[0] salt = get_salt(bot) key = PBKDF2(password, salt) text = " ".join(split[1:]) + # get the encoded IV from the database and decode it iv_encoded = db.execute("select iv from encryption where" " encrypted=?", (text,)).fetchone()[0] iv = base64.b64decode(iv_encoded) + # create AES cipher, decode text, decrypt text, and unpad it cipher = AES.new(key, AES.MODE_CBC, iv) return unpad(cipher.decrypt(base64.b64decode(text))) \ No newline at end of file From 47eae6255989055440b45cfe667c9a6f8a92ce27 Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Tue, 1 Oct 2013 14:57:02 +1300 Subject: [PATCH 5/5] Tidied code some more --- plugins/encrypt.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/plugins/encrypt.py b/plugins/encrypt.py index 7618cd5..95bdf44 100644 --- a/plugins/encrypt.py +++ b/plugins/encrypt.py @@ -8,14 +8,21 @@ import base64 import json import hashlib -BS = 16 +# helper functions to pad and unpad a string to a specified block size +# +BS = AES.block_size pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-ord(s[-1])] +# 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))) + db_ready = False + def db_init(db): - """check to see that our db has the the encryption table and return a connection.""" + """check to see that our db has the the encryption table.""" db.execute("create table if not exists encryption(encrypted, iv, " "primary key(encrypted))") db.commit() @@ -23,6 +30,7 @@ def db_init(db): def get_salt(bot): + """generate an encryption salt if none exists, then returns the salt""" 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) @@ -54,8 +62,7 @@ def encrypt(inp, bot=None, db=None, notice=None): # create the AES cipher and encrypt/encode the text with it text = " ".join(split[1:]) cipher = AES.new(key, AES.MODE_CBC, iv) - encrypted = cipher.encrypt(pad(text)) - encoded = base64.b64encode(encrypted) + encoded = encode_aes(cipher, text) # store the encoded text and IV in the DB for decoding later db.execute("insert or replace into encryption(encrypted, iv)" @@ -92,4 +99,4 @@ def decrypt(inp, bot=None, db=None, notice=None): # create AES cipher, decode text, decrypt text, and unpad it cipher = AES.new(key, AES.MODE_CBC, iv) - return unpad(cipher.decrypt(base64.b64decode(text))) \ No newline at end of file + return decode_aes(cipher, text) \ No newline at end of file