diff --git a/pbincli.py b/pbincli.py index e4c1eef..d5ea5ff 100755 --- a/pbincli.py +++ b/pbincli.py @@ -19,7 +19,7 @@ def main(): send_parser.add_argument("-d", "--discus", default=False, action="store_true", help="open discussion of sent paste") send_parser.add_argument("-e", "--expire", default="1day", action="store", help="expiration of paste (default: 1day)") send_parser.add_argument("-f", "--format", default="plaintext", action="store", help="format of paste (default: plaintext)") - send_parser.add_argument("-p", "--password", default=None, help="password for crypting paste") + send_parser.add_argument("-p", "--password", help="password for crypting paste") send_parser.add_argument("filename", help="filename (example: image.jpg)") send_parser.set_defaults(func=pbincli.actions.send) diff --git a/pbincli/actions.py b/pbincli/actions.py index bf67b7f..d47a91b 100644 --- a/pbincli/actions.py +++ b/pbincli/actions.py @@ -1,35 +1,35 @@ """Action functions for argparser""" -import base64 +import json +import os import pbincli.actions +'''from pbincli.sjcl_gcm import SJCL''' +import pbincli.sjcl_simple +from base64 import b64encode from Crypto.Hash import SHA256 -from Crypto.Random import get_random_bytes -from pbincli.sjcl_gcm import SJCL from pbincli.transports import privatebin from pbincli.utils import PBinCLIException, check_readable, check_writable from zlib import compress -import json def send(args): """ Sub-command for sending paste """ check_readable(args.filename) with open(args.filename, "rb") as f: contents = f.read() - file = base64.b64encode(compress(contents)) + file = b64encode(compress(contents)) - passphrase = base64.b64encode(get_random_bytes(32)) - if not args.password: - password = passphrase - else: + passphrase = os.urandom(32) + print("Passphrase: {}".format(passphrase)) + if args.password: p = SHA256.new() p.update(args.password.encode("UTF-8")) - password = passphrase + p.hexdigest().encode("UTF-8") + passphrase = passphrase + p.hexdigest().encode("UTF-8") + print("Password: {}".format(password)) + print(args.password) - data = SJCL().encrypt(file, password) - #request = "data={}&expire={}&formatter={}&burnafterreading={}&opendiscussion={}".format(json.dumps(data, ensure_ascii=False), args.expire, args.format, int(args.burn), int(args.discus)) + '''data = SJCL().encrypt(file, password.decode("UTF-8"))''' + data = pbincli.sjcl_simple.encrypt(password, file) request = {'data':json.dumps(data, ensure_ascii=False),'expire':args.expire,'formatter':args.format,'burnafterreading':int(args.burn),'opendiscussion':int(args.discus) } print(request) - '''Here we must run function post from pbincli.transports''' - print(request) - privatebin().post(request) + privatebin().post(request, passphrase) diff --git a/pbincli/sjcl_simple.py b/pbincli/sjcl_simple.py new file mode 100644 index 0000000..751ea1a --- /dev/null +++ b/pbincli/sjcl_simple.py @@ -0,0 +1,89 @@ +import os +from base64 import b64decode, b64encode + +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC +from cryptography.hazmat.backends import default_backend + + +_BACKEND = default_backend() + + +def decrypt(pwd, json): + iv = b64decode(json['iv']) + ct = b64decode(json['ct']) + salt = b64decode(json['salt']) + ts = data['ts'] / 8 + + tag_start = len(ct) - ts + tag = ct[tag_start:] + ciphertext = ct[:tag_start] + + mode_class = getattr(modes, json['mode'].upper()) + algo_class = getattr(algorithms, json['cipher'].upper()) + + kdf = _kdf(json['ks'], iters=json['iter'], salt=salt)[0] + key = kdf.derive(pwd) + cipher = Cipher(algo_class(key), + mode_class(iv, tag, min_tag_length=ts), + backend=_BACKEND) + + dec = cipher.decryptor() + result = dec.update(ciphertext) + dec.finalize() + return result + + +def encrypt(pwd, plaintext, mode='gcm', algorithm='aes', + keysize=256, tagsize=128, iters=256000): + ts = tagsize / 8 + + mode_class = getattr(modes, mode.upper()) + algo_class = getattr(algorithms, algorithm.upper()) + + iv = os.urandom(16) + kdf, salt = _kdf(keysize, iters) + key = kdf.derive(pwd) + cipher = Cipher(algo_class(key), + mode_class(iv, min_tag_length=ts), + backend=_BACKEND) + + enc = cipher.encryptor() + ciphertext = enc.update(plaintext) + enc.finalize() + + json = { + "v": 1, + "iv": b64encode(iv), + "salt": b64encode(salt), + "ct": b64encode(ciphertext + enc.tag[:ts]), + "iter": iters, + "ks": keysize, + "ts": tagsize, + "mode": mode, + "cipher": algorithm, + "adata": "" + } + return json + + +def _kdf(keysize=256, iters=256000, salt=None, **kwargs): + kdf_salt = salt or os.urandom(8) + print("Salt: {}".format(kdf_salt)) + kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), + length=keysize / 8, + salt=kdf_salt, + iterations=iters, + backend=_BACKEND) + + return kdf, kdf_salt + + +if __name__ == "__main__": + import json + + blob = '{"iv":"/6dKRRAOZ60oyumLMQsBtg==","v":1,"iter":256000,"ks":128,"ts":64,"mode":"gcm","adata":"","cipher":"aes","salt":"s8+LFcBmbcc=","ct":"wTapp5CWmD6SFA=="}' + data = json.loads(blob) + result = decrypt('pwd', data) + assert result == "hi" + + print(decrypt('pwd', encrypt('pwd', result, tagsize=64))) diff --git a/pbincli/transports.py b/pbincli/transports.py index 59e4437..f54f77c 100644 --- a/pbincli/transports.py +++ b/pbincli/transports.py @@ -9,8 +9,10 @@ class privatebin(object): self.server = 'http://paste.r4sas.i2p/' self.headers = {'X-Requested-With': 'JSONHttpRequest'} - def post(self, data): + def post(self, data, password): r = requests.post(url=self.server, headers=self.headers, proxies=self.proxies, data=data) - print(r.request) - print(r.status_code) - print(r.text) + print(r.text) + result = json.loads(r.text) + '''{"status":0,"id":"aaabbb","url":"\/?aaabbb","deletetoken":"aaabbbccc"}''' + if result['status'] == 0: + print("Paste uploaded!\nPasteID:\t{}\nPassword:\t{}\nDelete token:\t{}\n".format(result['id'], password.decode("UTF-8"), result['deletetoken'])) diff --git a/requirements.txt b/requirements.txt index 66cacfd..4ae6b83 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,5 @@ +appdirs +packaging +cryptography pycryptodome -requests \ No newline at end of file +requests