From 4c124a33c083529dc623697c059126cdcae2d8a6 Mon Sep 17 00:00:00 2001 From: r4sas Date: Mon, 3 Jun 2019 07:17:15 +0000 Subject: [PATCH] [v2] encode json for v2, move de/compress to Paste class (#13) --- pbincli/actions.py | 9 ++++----- pbincli/format.py | 46 +++++++++++++++++++++++++++++++++++----------- pbincli/utils.py | 21 +-------------------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/pbincli/actions.py b/pbincli/actions.py index db968d3..b56ee7e 100644 --- a/pbincli/actions.py +++ b/pbincli/actions.py @@ -33,8 +33,9 @@ def send(args, api_client): request = paste.getJSON() - if args.debug: print("Passphrase:\t{}".format(paste.getHash())) - if args.debug: print("Request:\t{}".format(request)) + if args.debug: + print("Passphrase:\t{}".format(paste.getHash())) + print("Request:\t{}".format(request)) # If we use dry option, exit now if args.dry: exit(0) @@ -76,7 +77,7 @@ def get(args, api_client): if args.debug: print("PasteID:\t{}\nPassphrase:\t{}".format(pasteid, passphrase)) - paste = Paste() + paste = Paste(args.debug) if args.password: paste.setPassword(args.password) @@ -90,11 +91,9 @@ def get(args, api_client): print("Paste received!") version = result['v'] if 'v' in result else 1 - if args.debug: print("Paste version:\t{}\n".format(version)) paste.setVersion(version) if version == 2: - #if args.debug: print("Message:\t{}\nAuthentication data:\t{}".format(result['ct'], result['adata'])) if args.debug: print("Authentication data:\t{}".format(result['adata'])) paste.setHash(passphrase) diff --git a/pbincli/format.py b/pbincli/format.py index bf4de13..6e37f21 100644 --- a/pbincli/format.py +++ b/pbincli/format.py @@ -64,7 +64,11 @@ class Paste: def getJSON(self): - return self._data + if self._version == 2: + from pbincli.utils import json_encode + return json_encode(self._data).decode() + else: + return self._data def loadJSON(self, data): @@ -110,8 +114,28 @@ class Paste: return cipher + def __decompress(self, s): + import zlib + if self._version == 2: + return zlib.decompress(s, -zlib.MAX_WBITS) + else: + return zlib.decompress(bytearray(map(ord, b64decode(s.encode('utf-8')).decode('utf-8'))), -zlib.MAX_WBITS) + + + def __compress(self, s): + import zlib + if self._version == 2: + # using compressobj as compress doesn't let us specify wbits + # needed to get the raw stream without headers + co = zlib.compressobj(wbits=-zlib.MAX_WBITS) + return co.compress(s) + co.flush() + else: + co = zlib.compressobj(wbits=-zlib.MAX_WBITS) + b = co.compress(s) + co.flush() + return b64encode(''.join(map(chr, b)).encode('utf-8')) + + def decrypt(self): - from pbincli.utils import decompress from json import loads as json_decode if self._version == 2: @@ -124,7 +148,7 @@ class Paste: cipher_text_tag = b64decode(self._data['ct']) cipher_text = cipher_text_tag[:-CIPHER_TAG_BYTES] cipher_tag = cipher_text_tag[-CIPHER_TAG_BYTES:] - cipher_message = json_decode(decompress(cipher.decrypt_and_verify(cipher_text, cipher_tag), self._version).decode()) + cipher_message = json_decode(self.__decompress(cipher.decrypt_and_verify(cipher_text, cipher_tag)).decode()) self._text = cipher_message['paste'].encode() if 'attachment' in cipher_message and 'attachment_name' in cipher_message: @@ -147,7 +171,7 @@ class Paste: text = SJCL().decrypt(cipher_text, password) if len(text): - self._text = decompress(text.decode(), self._version) + self._text = self.__decompress(text.decode()) if 'attachment' in self._data and 'attachmentname' in self._data: cipherfile = json_decode(self._data['attachment']) @@ -158,12 +182,12 @@ class Paste: attachment = SJCL().decrypt(cipherfile, password) attachmentname = SJCL().decrypt(cipherfilename, password) - self._attachment = decompress(attachment.decode('utf-8'), self._version).decode('utf-8') - self._attachment_name = decompress(attachmentname.decode('utf-8'), self._version).decode('utf-8') + self._attachment = self.__decompress(attachment.decode('utf-8')).decode('utf-8') + self._attachment_name = self.__decompress(attachmentname.decode('utf-8')).decode('utf-8') def encrypt(self, formatter, burnafterreading, discussion, expiration): - from pbincli.utils import compress, json_encode + from pbincli.utils import json_encode if self._version == 2: iv = get_random_bytes(CIPHER_TAG_BYTES) salt = get_random_bytes(CIPHER_SALT_BYTES) @@ -191,7 +215,7 @@ class Paste: cipher_message['attachment_name'] = self._attachment_name cipher = self.__initializeCipher(key, iv, adata) - ciphertext, tag = cipher.encrypt_and_digest(compress(json_encode(cipher_message), self._version)) + ciphertext, tag = cipher.encrypt_and_digest(self.__compress(json_encode(cipher_message))) self._data = {'v':2,'adata':adata,'ct':b64encode(ciphertext + tag).decode(),'meta':{'expire':expiration}} @@ -210,16 +234,16 @@ class Paste: if self._debug: print("Password:\t{}".format(password)) # Encrypting text - cipher = SJCL().encrypt(compress(self._text.encode('utf-8'), self._version), password, mode='gcm') + cipher = SJCL().encrypt(self.__compress(self._text.encode('utf-8')), password, mode='gcm') for k in ['salt', 'iv', 'ct']: cipher[k] = cipher[k].decode() self._data['data'] = json_encode(cipher) if self._attachment: - cipherfile = SJCL().encrypt(compress(self._attachment.encode('utf-8'), self._version), password, mode='gcm') + cipherfile = SJCL().encrypt(self.__compress(self._attachment.encode('utf-8')), password, mode='gcm') for k in ['salt', 'iv', 'ct']: cipherfile[k] = cipherfile[k].decode() - cipherfilename = SJCL().encrypt(compress(self._attachment_name.encode('utf-8'), self._version), password, mode='gcm') + cipherfilename = SJCL().encrypt(self.__compress(self._attachment_name.encode('utf-8')), password, mode='gcm') for k in ['salt', 'iv', 'ct']: cipherfilename[k] = cipherfilename[k].decode() self._data['attachment'] = json_encode(cipherfile) diff --git a/pbincli/utils.py b/pbincli/utils.py index 2028860..4c36664 100644 --- a/pbincli/utils.py +++ b/pbincli/utils.py @@ -1,4 +1,4 @@ -import json, ntpath, os, zlib +import json, ntpath, os from base64 import b64encode, b64decode class PBinCLIException(Exception): @@ -22,24 +22,5 @@ def check_writable(f): raise PBinCLIException("Path is not writable: {}".format(f)) -def decompress(s, ver = 1): - if ver == 2: - return zlib.decompress(s, -zlib.MAX_WBITS) - else: - return zlib.decompress(bytearray(map(ord, b64decode(s.encode('utf-8')).decode('utf-8'))), -zlib.MAX_WBITS) - - -def compress(s, ver = 1): - if ver == 2: - # using compressobj as compress doesn't let us specify wbits - # needed to get the raw stream without headers - co = zlib.compressobj(wbits=-zlib.MAX_WBITS) - return co.compress(s) + co.flush() - else: - co = zlib.compressobj(wbits=-zlib.MAX_WBITS) - b = co.compress(s) + co.flush() - return b64encode(''.join(map(chr, b)).encode('utf-8')) - - def json_encode(s): return json.dumps(s, separators=(',',':')).encode()