diff --git a/pappyproxy/config.py b/pappyproxy/config.py index 04abe1d..32dfeae 100644 --- a/pappyproxy/config.py +++ b/pappyproxy/config.py @@ -164,6 +164,7 @@ class PappyConfig(object): self.global_config_dict = {} self.archive = 'project.archive' + self.debug = False self.crypt_dir = 'crypt' self.crypt_file = 'project.crypt' self.crypt_session = False diff --git a/pappyproxy/crypto.py b/pappyproxy/crypto.py index 79aed48..17cece2 100644 --- a/pappyproxy/crypto.py +++ b/pappyproxy/crypto.py @@ -32,28 +32,33 @@ class Crypto(object): """ # Leave the crypto working directory - os.chdir('../') + if self.config.crypt_dir in os.getcwd(): + os.chdir('../') self.compressor.compress_project() + # Get the password and salt, then derive the key + self.crypto_ramp_up() + # Create project and crypto archive archive_file = open(self.archive, 'rb') archive_crypt = open(self.config.crypt_file, 'wb') - # Get the password and salt, then derive the key - self.crypto_ramp_up() + try: + # Encrypt the archive read as a bytestring + fern = Fernet(self.key) + crypt_token = fern.encrypt(archive_file.read()) + archive_crypt.write(crypt_token) + except InvalidToken as e: + raise PappyException("Error encrypting project: ", e) + return False - # Encrypt the archive read as a bytestring - fern = Fernet(self.key) - crypt_token = fern.encrypt(archive_file.read()) - archive_crypt.write(crypt_token) + archive_file.close() + archive_crypt.close() # Store the salt for the next decryption self.create_salt_file() - archive_file.close() - archive_crypt.close() - # Delete clear-text files self.delete_clear_files() return True @@ -79,6 +84,7 @@ class Crypto(object): cf = self.config.crypt_file sl = self.config.salt_len crl = os.path.getsize(cf) - sl + archive_crypt = open(cf, 'rb').read(crl) archive_file = open(self.config.archive, 'wb') @@ -89,18 +95,16 @@ class Crypto(object): fern = Fernet(self.key) archive = fern.decrypt(archive_crypt) break - except InvalidToken: - print "Invalid password" + except InvalidToken as e: + print "Invalid decryption: ", e retries -= 1 # Quit pappy if user doesn't retry # or if all retries exhuasted if not self.confirm_password_retry() or retries <= 0: - self.config.crypt_success = False return False else: self.password = None self.key = None - self.salt = None pass archive_file.write(archive) @@ -112,7 +116,7 @@ class Crypto(object): return True def confirm_password_retry(self): - answer = raw_input("Re-enter your password? (y/n)").strip() + answer = raw_input("Re-enter your password? (y/n): ").strip() if answer[0] == "y" or answer[0] == "Y": return True else: @@ -121,7 +125,8 @@ class Crypto(object): def crypto_ramp_up(self): if not self.password: self.get_password() - self.set_salt() + if not self.salt: + self.set_salt() self.derive_key() def get_password(self): @@ -137,7 +142,11 @@ class Crypto(object): raise PappyException("Invalid password, try again") def set_salt(self): - if os.path.isfile(self.config.crypt_file): + if self.config.crypt_dir in os.getcwd(): + os.chdir('../') + self.set_salt_from_file() + os.chdir(self.config.crypt_dir) + elif os.path.isfile(self.config.crypt_file): self.set_salt_from_file() else: self.salt = os.urandom(16) @@ -152,19 +161,17 @@ class Crypto(object): # behavior. salt_file = open(self.config.crypt_file, 'rb') sl = self.config.salt_len - salt_file.seek(sl, 2) + # Negate the salt length to seek to the + # correct position in the buffer + salt_file.seek(-sl, 2) self.salt = salt_file.read(sl) + salt_file.close() except: cf = self.config.crypt_file raise PappyException("Unable to read %s" % cf) def create_salt_file(self): - # WARNING: must open `crypt_file` in `wb` mode - # or `salt_file.seek()` will result in undefined - # behavior. - salt_file = open(self.config.crypt_file, 'wb') - # Seek to the end of the encrypted archive - salt_file.seek(0,2) + salt_file = open(self.config.crypt_file, 'a') salt_file.write(self.salt) salt_file.close() diff --git a/pappyproxy/pappy.py b/pappyproxy/pappy.py index 83882d6..fd20b21 100755 --- a/pappyproxy/pappy.py +++ b/pappyproxy/pappy.py @@ -76,6 +76,9 @@ class PappySession(object): self.config.load_from_file('./config.json') self.config.global_load_from_file() self.delete_data_on_quit = False + else: + self.complete_defer.callback(None) + return # If the data file doesn't exist, create it with restricted permissions if not os.path.isfile(self.config.datafile): @@ -153,10 +156,7 @@ class PappySession(object): if self.crypto.encrypt_project(): return True else: - errmsg = "There was an issue encrypting the project." - raise PappyException(errmsg) - reactor.stop() - defer.returnValue(None) + return False def decrypt(self): # Attempt to decrypt project archive @@ -164,10 +164,7 @@ class PappySession(object): return True # Quit pappy on failure else: - errmsg = "There was an issue encrypting the project." - raise PappyException(errmsg) - reactor.stop() - defer.returnValue(None) + return False @defer.inlineCallbacks def cleanup(self, ignored=None): @@ -187,6 +184,7 @@ def parse_args(): parser = argparse.ArgumentParser(description='An intercepting proxy for testing web applications.') parser.add_argument('-l', '--lite', help='Run the proxy in "lite" mode', action='store_true') + parser.add_argument('-d', '--debug', help='Run the proxy in "debug" mode', action='store_true') try: hlpmsg = ''.join(['Start pappy in "crypto" mode,', 'must supply a name for the encrypted', @@ -211,6 +209,10 @@ def parse_args(): else: settings['crypt'] = None + if args.debug: + settings['debug'] = True + else: + settings['debug'] = False return settings def set_text_factory(conn): @@ -257,6 +259,9 @@ def main(): pappy_config.global_load_from_file() session.delete_data_on_quit = False + if settings['debug']: + pappy_config.debug = True + yield session.start() session.complete_defer.addCallback(lambda ignored: reactor.stop())