Pep8 corrections
This commit is contained in:
parent
f5c53add9c
commit
f7d8df69cc
3 changed files with 75 additions and 69 deletions
|
@ -14,7 +14,8 @@ except ImportError:
|
||||||
print "BZ2 not installed on your system"
|
print "BZ2 not installed on your system"
|
||||||
|
|
||||||
from base64 import b64encode, b64decode
|
from base64 import b64encode, b64decode
|
||||||
from os import getcwd, sep, path, urandom
|
from os import getcwd, sep, path, urandom
|
||||||
|
|
||||||
|
|
||||||
class Compress(object):
|
class Compress(object):
|
||||||
def __init__(self, sessconfig):
|
def __init__(self, sessconfig):
|
||||||
|
@ -27,17 +28,17 @@ class Compress(object):
|
||||||
self.tar_project()
|
self.tar_project()
|
||||||
else:
|
else:
|
||||||
self.zip_project()
|
self.zip_project()
|
||||||
|
|
||||||
def decompress_project(self):
|
def decompress_project(self):
|
||||||
if bz2:
|
if bz2:
|
||||||
self.untar_project()
|
self.untar_project()
|
||||||
else:
|
else:
|
||||||
self.unzip_project()
|
self.unzip_project()
|
||||||
|
|
||||||
def zip_project(self):
|
def zip_project(self):
|
||||||
"""
|
"""
|
||||||
Zip project files
|
Zip project files
|
||||||
|
|
||||||
Using append mode (mode='a') will create a zip archive
|
Using append mode (mode='a') will create a zip archive
|
||||||
if none exists in the project.
|
if none exists in the project.
|
||||||
"""
|
"""
|
||||||
|
@ -47,32 +48,32 @@ class Compress(object):
|
||||||
zf.close()
|
zf.close()
|
||||||
except zipfile.LargeZipFile as e:
|
except zipfile.LargeZipFile as e:
|
||||||
raise PappyException("Project zipfile too large. Error: ", e)
|
raise PappyException("Project zipfile too large. Error: ", e)
|
||||||
|
|
||||||
def unzip_project(self):
|
def unzip_project(self):
|
||||||
"""
|
"""
|
||||||
Extract project files from decrypted zip archive.
|
Extract project files from decrypted zip archive.
|
||||||
Initially checks the zip archive's magic number and
|
Initially checks the zip archive's magic number and
|
||||||
attempts to extract pappy.json to validate integrity
|
attempts to extract pappy.json to validate integrity
|
||||||
of the zipfile.
|
of the zipfile.
|
||||||
"""
|
"""
|
||||||
if not zipfile.is_zipfile(self.zip_archive):
|
if not zipfile.is_zipfile(self.zip_archive):
|
||||||
raise PappyException("Project archive corrupted.")
|
raise PappyException("Project archive corrupted.")
|
||||||
|
|
||||||
zf = zipfile.ZipFile(self.zip_archive)
|
zf = zipfile.ZipFile(self.zip_archive)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
zf.extract("config.json")
|
zf.extract("config.json")
|
||||||
except zipfile.BadZipfile as e:
|
except zipfile.BadZipfile as e:
|
||||||
raise PappyException("Project archive contents corrupted. Error: ", e)
|
raise PappyException("Zip archive corrupted. Error: ", e)
|
||||||
|
|
||||||
zf.extractall()
|
zf.extractall()
|
||||||
|
|
||||||
def tar_project(self):
|
def tar_project(self):
|
||||||
archive = tarfile.open(self.bz2_archive, 'w:bz2')
|
archive = tarfile.open(self.bz2_archive, 'w:bz2')
|
||||||
|
|
||||||
archive.add(self.config.crypt_dir)
|
archive.add(self.config.crypt_dir)
|
||||||
archive.close()
|
archive.close()
|
||||||
|
|
||||||
def untar_project(self):
|
def untar_project(self):
|
||||||
if tarfile.is_tarfile(self.bz2_archive):
|
if tarfile.is_tarfile(self.bz2_archive):
|
||||||
# Raise exception if there is a failure
|
# Raise exception if there is a failure
|
||||||
|
@ -80,4 +81,4 @@ class Compress(object):
|
||||||
with tarfile.open(self.bz2_archive, "r:bz2") as archive:
|
with tarfile.open(self.bz2_archive, "r:bz2") as archive:
|
||||||
archive.extractall()
|
archive.extractall()
|
||||||
except tarfile.ExtractError as e:
|
except tarfile.ExtractError as e:
|
||||||
raise PappyException("Project archive contents corrupted. Error: ", e)
|
raise PappyException("Tar archive corrupted. Error: ", e)
|
||||||
|
|
|
@ -14,53 +14,55 @@ from base64 import b64encode, b64decode
|
||||||
from cryptography.fernet import Fernet, InvalidToken
|
from cryptography.fernet import Fernet, InvalidToken
|
||||||
from twisted.internet import reactor, defer
|
from twisted.internet import reactor, defer
|
||||||
|
|
||||||
|
|
||||||
class Crypto(object):
|
class Crypto(object):
|
||||||
def __init__(self, sessconfig):
|
def __init__(self, sessconfig):
|
||||||
self.config = sessconfig
|
self.config = sessconfig
|
||||||
self.archive = sessconfig.archive
|
self.archive = sessconfig.archive
|
||||||
self.compressor = compress.Compress(sessconfig)
|
self.compressor = compress.Compress(sessconfig)
|
||||||
self.key = None
|
self.key = None
|
||||||
self.password = None
|
self.password = None
|
||||||
self.salt = None
|
self.salt = None
|
||||||
|
|
||||||
def encrypt_project(self):
|
def encrypt_project(self):
|
||||||
"""
|
"""
|
||||||
Compress and encrypt the project files, deleting clear-text files afterwards
|
Compress and encrypt the project files,
|
||||||
|
deleting clear-text files afterwards
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Leave the crypto working directory
|
# Leave the crypto working directory
|
||||||
os.chdir('../')
|
os.chdir('../')
|
||||||
|
|
||||||
self.compressor.compress_project()
|
self.compressor.compress_project()
|
||||||
|
|
||||||
# Create project and crypto archive
|
# Create project and crypto archive
|
||||||
archive_file = open(self.archive, 'rb')
|
archive_file = open(self.archive, 'rb')
|
||||||
archive_crypt = open(self.config.crypt_file, 'wb')
|
archive_crypt = open(self.config.crypt_file, 'wb')
|
||||||
|
|
||||||
# Get the password and salt, then derive the key
|
# Get the password and salt, then derive the key
|
||||||
self.crypto_ramp_up()
|
self.crypto_ramp_up()
|
||||||
|
|
||||||
# Encrypt the archive read as a bytestring
|
# Encrypt the archive read as a bytestring
|
||||||
fern = Fernet(self.key)
|
fern = Fernet(self.key)
|
||||||
crypt_token = fern.encrypt(archive_file.read())
|
crypt_token = fern.encrypt(archive_file.read())
|
||||||
archive_crypt.write(crypt_token)
|
archive_crypt.write(crypt_token)
|
||||||
|
|
||||||
# Store the salt for the next decryption
|
# Store the salt for the next decryption
|
||||||
self.create_salt_file()
|
self.create_salt_file()
|
||||||
|
|
||||||
archive_file.close()
|
archive_file.close()
|
||||||
archive_crypt.close()
|
archive_crypt.close()
|
||||||
|
|
||||||
# Delete clear-text files
|
# Delete clear-text files
|
||||||
self.delete_clear_files()
|
self.delete_clear_files()
|
||||||
|
|
||||||
|
|
||||||
def decrypt_project(self):
|
def decrypt_project(self):
|
||||||
"""
|
"""
|
||||||
Decrypt and decompress the project files
|
Decrypt and decompress the project files
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# If project hasn't been encrypted before, setup crypt working directory
|
# If project hasn't been encrypted before,
|
||||||
|
# setup crypt working directory
|
||||||
if not os.path.isfile(self.config.crypt_file):
|
if not os.path.isfile(self.config.crypt_file):
|
||||||
os.mkdir(self.config.crypt_dir)
|
os.mkdir(self.config.crypt_dir)
|
||||||
|
|
||||||
|
@ -69,17 +71,17 @@ class Crypto(object):
|
||||||
shutil.copy2(pf, self.config.crypt_dir)
|
shutil.copy2(pf, self.config.crypt_dir)
|
||||||
os.chdir(self.config.crypt_dir)
|
os.chdir(self.config.crypt_dir)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Otherwise, decrypt and decompress the project
|
# Otherwise, decrypt and decompress the project
|
||||||
else:
|
else:
|
||||||
archive_crypt = open(self.config.crypt_file, 'rb').read()
|
archive_crypt = open(self.config.crypt_file, 'rb').read()
|
||||||
archive_file = open(self.config.archive, 'wb')
|
archive_file = open(self.config.archive, 'wb')
|
||||||
|
|
||||||
retries = 3
|
retries = 3
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self.crypto_ramp_up()
|
self.crypto_ramp_up()
|
||||||
fern = Fernet(self.key)
|
fern = Fernet(self.key)
|
||||||
archive = fern.decrypt(archive_crypt)
|
archive = fern.decrypt(archive_crypt)
|
||||||
break
|
break
|
||||||
except InvalidToken:
|
except InvalidToken:
|
||||||
|
@ -93,35 +95,35 @@ class Crypto(object):
|
||||||
self.password = None
|
self.password = None
|
||||||
self.key = None
|
self.key = None
|
||||||
self.salt = None
|
self.salt = None
|
||||||
pass
|
pass
|
||||||
|
|
||||||
archive_file.write(archive)
|
archive_file.write(archive)
|
||||||
archive_file.close()
|
archive_file.close()
|
||||||
|
|
||||||
self.compressor.decompress_project()
|
self.compressor.decompress_project()
|
||||||
|
|
||||||
# Force generation of new salt and crypt archive
|
# Force generation of new salt and crypt archive
|
||||||
self.delete_crypt_files()
|
self.delete_crypt_files()
|
||||||
|
|
||||||
os.chdir(self.config.crypt_dir)
|
os.chdir(self.config.crypt_dir)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def confirm_password_retry(self):
|
def confirm_password_retry(self):
|
||||||
answer = raw_input("Would you like to 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":
|
if answer[0] == "y" or answer[0] == "Y":
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def crypto_ramp_up(self):
|
def crypto_ramp_up(self):
|
||||||
if not self.password:
|
if not self.password:
|
||||||
self.get_password()
|
self.get_password()
|
||||||
self.set_salt()
|
self.set_salt()
|
||||||
self.derive_key()
|
self.derive_key()
|
||||||
|
|
||||||
def get_password(self):
|
def get_password(self):
|
||||||
"""
|
"""
|
||||||
Retrieve password from the user. Raise an exception if the
|
Retrieve password from the user. Raise an exception if the
|
||||||
password is not capable of utf-8 encoding.
|
password is not capable of utf-8 encoding.
|
||||||
"""
|
"""
|
||||||
encoded_passwd = ""
|
encoded_passwd = ""
|
||||||
|
@ -130,75 +132,75 @@ class Crypto(object):
|
||||||
self.password = passwd.encode("utf-8")
|
self.password = passwd.encode("utf-8")
|
||||||
except:
|
except:
|
||||||
raise PappyException("Invalid password, try again")
|
raise PappyException("Invalid password, try again")
|
||||||
|
|
||||||
def set_salt(self):
|
def set_salt(self):
|
||||||
if os.path.isfile(self.config.salt_file):
|
if os.path.isfile(self.config.salt_file):
|
||||||
self.set_salt_from_file()
|
self.set_salt_from_file()
|
||||||
else:
|
else:
|
||||||
self.salt = os.urandom(16)
|
self.salt = os.urandom(16)
|
||||||
|
|
||||||
def set_salt_from_file(self):
|
def set_salt_from_file(self):
|
||||||
try:
|
try:
|
||||||
salt_file = open(self.config.salt_file, 'rb')
|
salt_file = open(self.config.salt_file, 'rb')
|
||||||
self.salt = salt_file.readline().strip()
|
self.salt = salt_file.readline().strip()
|
||||||
except:
|
except:
|
||||||
raise PappyException("Unable to read project.salt")
|
raise PappyException("Unable to read project.salt")
|
||||||
|
|
||||||
def create_salt_file(self):
|
def create_salt_file(self):
|
||||||
salt_file = open(self.config.salt_file, 'wb')
|
salt_file = open(self.config.salt_file, 'wb')
|
||||||
|
|
||||||
salt_file.write(self.salt)
|
salt_file.write(self.salt)
|
||||||
salt_file.close()
|
salt_file.close()
|
||||||
|
|
||||||
def derive_key(self):
|
def derive_key(self):
|
||||||
"""
|
"""
|
||||||
Derive a key sufficient for use as a cryptographic key
|
Derive a key sufficient for use as a cryptographic key
|
||||||
used to encrypt the project (currently: cryptography.Fernet).
|
used to encrypt the project (currently: cryptography.Fernet).
|
||||||
|
|
||||||
cryptography.Fernet utilizes AES-CBC-128, requiring a 32-byte key.
|
cryptography.Fernet utilizes AES-CBC-128, requiring a 32-byte key.
|
||||||
Parameter notes from the py-scrypt source-code:
|
Parameter notes from the py-scrypt source-code:
|
||||||
https://bitbucket.org/mhallin/py-scrypt/
|
https://bitbucket.org/mhallin/py-scrypt/
|
||||||
|
|
||||||
Compute scrypt(password, salt, N, r, p, buflen).
|
Compute scrypt(password, salt, N, r, p, buflen).
|
||||||
|
|
||||||
The parameters r, p, and buflen must satisfy r * p < 2^30 and
|
The parameters r, p, and buflen must satisfy r * p < 2^30 and
|
||||||
buflen <= (2^32 - 1) * 32. The parameter N must be a power of 2
|
buflen <= (2^32 - 1) * 32. The parameter N must be a power of 2
|
||||||
greater than 1. N, r and p must all be positive.
|
greater than 1. N, r and p must all be positive.
|
||||||
|
|
||||||
Notes for Python 2:
|
Notes for Python 2:
|
||||||
- `password` and `salt` must be str instances
|
- `password` and `salt` must be str instances
|
||||||
- The result will be a str instance
|
- The result will be a str instance
|
||||||
|
|
||||||
Notes for Python 3:
|
Notes for Python 3:
|
||||||
- `password` and `salt` can be both str and bytes. If they are str
|
- `password` and `salt` can be both str and bytes. If they are str
|
||||||
instances, they wil be encoded with utf-8.
|
instances, they wil be encoded with utf-8.
|
||||||
- The result will be a bytes instance
|
- The result will be a bytes instance
|
||||||
|
|
||||||
Exceptions raised:
|
Exceptions raised:
|
||||||
- TypeError on invalid input
|
- TypeError on invalid input
|
||||||
- scrypt.error if scrypt failed
|
- scrypt.error if scrypt failed
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not self.key:
|
if not self.key:
|
||||||
self.key = b64encode(scrypt.hash(self.password, self.salt, buflen=32))
|
shash = scrypt.hash(self.password, self.salt, buflen=32)
|
||||||
|
self.key = b64encode(shash)
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
raise PappyException("Scrypt failed with type error: ", e)
|
raise PappyException("Scrypt failed with type error: ", e)
|
||||||
except scrypt.error, e:
|
except scrypt.error, e:
|
||||||
raise PappyException("Scrypt failed with internal error: ", e)
|
raise PappyException("Scrypt failed with internal error: ", e)
|
||||||
|
|
||||||
def delete_clear_files(self):
|
def delete_clear_files(self):
|
||||||
"""
|
"""
|
||||||
Deletes all clear-text files left in the project directory.
|
Deletes all clear-text files left in the project directory.
|
||||||
"""
|
"""
|
||||||
shutil.rmtree(self.config.crypt_dir)
|
shutil.rmtree(self.config.crypt_dir)
|
||||||
os.remove(self.config.archive)
|
os.remove(self.config.archive)
|
||||||
|
|
||||||
def delete_crypt_files(self):
|
def delete_crypt_files(self):
|
||||||
"""
|
"""
|
||||||
Deletes all encrypted-text files in the project directory.
|
Deletes all encrypted-text files in the project directory.
|
||||||
Forces generation of new salt after opening and closing the project.
|
Forces generation of new salt after opening and closing the project.
|
||||||
Adds security in the case of a one-time compromise of the system.
|
Adds security in the case of a one-time compromise of the system.
|
||||||
"""
|
"""
|
||||||
#os.remove(self.config.salt_file)
|
|
||||||
os.remove(self.config.crypt_file)
|
os.remove(self.config.crypt_file)
|
||||||
|
|
|
@ -159,9 +159,9 @@ class PappySession(object):
|
||||||
yield True
|
yield True
|
||||||
# Quit pappy on failure
|
# Quit pappy on failure
|
||||||
else:
|
else:
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
defer.returnValue(None)
|
defer.returnValue(None)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def cleanup(self, ignored=None):
|
def cleanup(self, ignored=None):
|
||||||
for port in self.ports:
|
for port in self.ports:
|
||||||
|
@ -181,10 +181,13 @@ def parse_args():
|
||||||
parser = argparse.ArgumentParser(description='An intercepting proxy for testing web applications.')
|
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('-l', '--lite', help='Run the proxy in "lite" mode', action='store_true')
|
||||||
try:
|
try:
|
||||||
parser.add_argument('-c', '--crypt', type=str, nargs=1, help='Start pappy in "crypto" mode, must supply a name for the encrypted project archive [CRYPT]')
|
hlpmsg = 'Start pappy in "crypto" mode,'+
|
||||||
|
'must supply a name for the encrypted'+
|
||||||
|
'project archive [CRYPT]'
|
||||||
|
parser.add_argument('-c', '--crypt', type=str, nargs=1, help=hlpmsg)
|
||||||
except:
|
except:
|
||||||
print 'Must supply a project name: pappy -c <project_name>'
|
print 'Must supply a project name: pappy -c <project_name>'
|
||||||
reactor.stop()
|
reactor.stop()
|
||||||
defer.returnValue(None)
|
defer.returnValue(None)
|
||||||
|
|
||||||
args = parser.parse_args(sys.argv[1:])
|
args = parser.parse_args(sys.argv[1:])
|
||||||
|
@ -197,7 +200,7 @@ def parse_args():
|
||||||
|
|
||||||
if args.crypt:
|
if args.crypt:
|
||||||
# Convert from single-item list produced by argparse `nargs=1`
|
# Convert from single-item list produced by argparse `nargs=1`
|
||||||
settings['crypt'] = args.crypt[0].encode('utf-8')
|
settings['crypt'] = args.crypt[0].encode('utf-8')
|
||||||
else:
|
else:
|
||||||
settings['crypt'] = None
|
settings['crypt'] = None
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue