@ -17,7 +17,7 @@ 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 = self . config . archive
self . archive = sess config. archive
self . compressor = compress . Compress ( sessconfig )
self . compressor = compress . Compress ( sessconfig )
self . key = None
self . key = None
self . password = None
self . password = None
@ -31,15 +31,15 @@ class Crypto(object):
# Leave the crypto working directory
# Leave the crypto working directory
os . chdir ( ' ../ ' )
os . chdir ( ' ../ ' )
# Get the password and salt, then derive the key
self . crypto_ramp_up ( )
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
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 ( ) )
@ -61,46 +61,64 @@ class Crypto(object):
"""
"""
# If project hasn't been encrypted before, setup crypt working directory
# If project hasn't been encrypted before, setup crypt working directory
crypt_fp = os . path . join ( os . getcwd ( ) , self . config . crypt_file )
if not os . path . isfile ( self . config . crypt_file ) :
if not os . path . isfile ( crypt_fp ) :
os . mkdir ( self . config . crypt_dir )
os . mkdir ( self . config . crypt_dir )
project_files = self . config . get_project_files ( )
project_files = self . config . get_project_files ( )
for pf in project_files :
for pf in project_files :
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
# Otherwise, decrypt and decompress the project
# Otherwise, decrypt and decompress the project
else :
else :
self . crypto_ramp_up ( )
fern = Fernet ( self . key )
# Decrypt the project archive
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 ' )
try :
archive = fern . decrypt ( archive_crypt )
retries = 3
except InvalidToken :
while True :
raise PappyException ( " Problem decrypting the file, restart pappy to try again " )
try :
reactor . stop ( )
self . crypto_ramp_up ( )
defer . returnValue ( None )
fern = Fernet ( self . key )
archive = fern . decrypt ( archive_crypt )
break
except InvalidToken :
print " Invalid password "
retries - = 1
# Quit pappy if user doesn't retry
# or if all retries exhuasted
if not self . confirm_password_retry ( ) or retries < = 0 :
return False
else :
self . password = None
self . key = None
self . salt = None
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
def confirm_password_retry ( self ) :
answer = raw_input ( " Would you like to re-enter your password? (y/n) " ) . strip ( )
if answer [ 0 ] == " y " or answer [ 0 ] == " Y " :
return True
else :
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
@ -108,24 +126,24 @@ class Crypto(object):
"""
"""
encoded_passwd = " "
encoded_passwd = " "
try :
try :
passwd = raw_input ( " Enter a password: " )
passwd = raw_input ( " Enter a password: " ) . strip ( )
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 ' )