#!/usr/local/bin/python -Ou """ enc - encrypt/decrypt files using one of SSLeay's ciphers. Copyright (c) 1998 by Marc-Andre Lemburg; All Rights Reserved; mailto:mal@lemburg.com; See the documentation for further copyright information or contact the author. DISCLAIMER & WARNING: This tool comes with NO WARRANTY. Use at YOUR OWN RISK. It may destroy data ! There is NO way to recover a forgotten pass phrase ! """ import exceptions,os,string,time,sys from CryptoWorld import Ciphers,Hashes,Utils from CommandLine import Application,SwitchOption,ArgumentOption # Globals verbose = 0 # Maximum block size used for en/decryption MAX_BLOCKSIZE = 1024 * 1000 class OperationalError(exceptions.StandardError): pass def filesize(file): oldpos = file.tell() file.seek(0,2) size = file.tell() file.seek(oldpos) return size def invisible_input(prompt='>>> '): """ Adapted from the Python 1.5.1 docs example getpass() """ import termios,TERMIOS fd = sys.stdin.fileno() old = termios.tcgetattr(fd) new = termios.tcgetattr(fd) new[3] = new[3] & ~TERMIOS.ECHO # fix lflags try: termios.tcsetattr(fd, TERMIOS.TCSADRAIN, new) passwd = raw_input(prompt) finally: termios.tcsetattr(fd, TERMIOS.TCSADRAIN, old) print return passwd def tempfile(filename='tmp', maxint=sys.maxint,time=time.time,int=int,hex=hex, exists=os.path.exists): """ Return a new filename for a temporary file (based on filename). """ temp = filename + '.' + hex(maxint % int(time())) + '.tmp' if not exists(temp): return temp # Ok, find an alternative name i = 0 while 1: temp = '%s.%s-%i.tmp' % (filename,hex(maxint % int(time())),i) if not exists(temp): return temp i = i + 1 # Global key _key = '' def get_cipher(name,check=0): global _key cc = getattr(Ciphers,name) keysize = cc.keysize if not _key: while 1: key1 = invisible_input('Please enter the key phrase: ') if check: key2 = invisible_input('Please reenter the phrase: ') if key1 != key2: print "Phrases don't match. Please start again..." continue if len(key1) == 0: print "Empty key phrase. Please start again..." else: break _key = key1 key = _key # Fit key if keysize > 0: if len(key) < keysize: key = key + \ 'Do not change this string, it is important !'\ [:keysize - len(key)] elif len(key) > keysize: key = key[:keysize] cipher = cc(key,Ciphers.CBC) return cipher def reset_key(): global _key _key = '' ### def encrypt(filename,ciphername,overwrite=0): if verbose: print 'Encrypting:',filename if filename[-4:] == '.enc': raise OperationalError,'already encrypted' if not os.path.isfile(filename): raise OperationalError,'not a file or not found' # Check overwrites if os.path.exists(filename + '.enc'): if not overwrite: raise OperationalError,'would overwrite an existing file' elif os.path.samefile(filename, filename + '.enc'): raise OperationalError,'would overwrite the original file' # Open plain file f = open(filename,'rb') size = filesize(f) if verbose: print ' total size: %i bytes' % size # Open work file workfilename = tempfile(filename) out = open(workfilename,'wb') try: # Init cipher and write header cipher = get_cipher(ciphername,check=1) out.write('enc %s %s %i\n' % \ (repr(filename),ciphername,size)) # Init hash and blocksize hash = Hashes.MD5() blocksize = size if blocksize > MAX_BLOCKSIZE: blocksize = MAX_BLOCKSIZE blocksize = ((blocksize + cipher.blocksize - 1) / cipher.blocksize) \ * cipher.blocksize # Write the encrypted data in blocks bytesread = 0 while bytesread < size: if verbose: print ' reading %i bytes...' % blocksize, block = f.read(blocksize) if verbose: print 'read %i bytes' % len(block) bytesread = bytesread + len(block) hash.update(block) if bytesread == size: # Final block offset = len(block) % cipher.blocksize if offset: padsize = cipher.blocksize - offset block = block + '\0'*padsize if verbose: print ' padding with %i bytes' % (padsize) encblock = cipher.encrypt(block) out.write(encblock) # Write hash value hash_value = hash.digest() if verbose: print ' hash value:',repr(hash_value) out.write(hash_value) # Copy work file to .enc file out.close() f.close() os.rename(workfilename,filename+'.enc') workfilename = None finally: if workfilename: if not out.closed: out.close() os.remove(workfilename) ### def decrypt(filename,overwrite=0): if verbose: print 'Decrypting:',filename if filename[-4:] != '.enc': raise OperationalError,'decrypt a plain file' if not os.path.isfile(filename): raise OperationalError,'not a file or not found' # Read header from cipher file f = open(filename,'rb') header = string.split(f.readline()) if len(header) != 4: raise OperationalError,'wrong header format:'+ str(header) origfilename = eval(header[1]) ciphername = header[2] size = string.atoi(header[3]) if verbose: print ' total size: %i bytes' % size # Check overwrites if os.path.exists(origfilename): if not overwrite: raise OperationalError,'would overwrite an existing file' elif os.path.samefile(origfilename, filename): raise OperationalError,'would overwrite the encrypted file' # Open work file workfilename = tempfile(filename) out = open(workfilename,'wb') try: # Load cipher and init hash cipher = get_cipher(ciphername) hash = Hashes.MD5() # Read the encrypted data in blocks blocksize = size if blocksize > MAX_BLOCKSIZE: blocksize = MAX_BLOCKSIZE blocksize = ((blocksize + cipher.blocksize - 1) / cipher.blocksize) \ * cipher.blocksize bytesread = 0 while bytesread < size: if size - bytesread < blocksize: # Read remaining data only blocksize = size - bytesread blocksize = ((blocksize + cipher.blocksize - 1) / \ cipher.blocksize) * cipher.blocksize if verbose: print ' reading %i bytes...' % blocksize, encblock = f.read(blocksize) if verbose: print 'read %i bytes' % len(encblock) bytesread = bytesread + len(encblock) block = cipher.decrypt(encblock) if bytesread > size: # Depad padsize = bytesread - size block = block[:-padsize] if verbose: print ' depadded last block by %i bytes' % (padsize) hash.update(block) out.write(block) # Check hash value hash_value = f.read(hash.digestsize) if verbose: print ' hash value:',repr(hash_value) if hash_value != hash.digest(): raise OperationalError,'data corrupt' # Copy workfile to origfile out.close() f.close() os.rename(workfilename,origfilename) workfilename = None finally: if workfilename: if not out.closed: out.close() os.remove(workfilename) ### class Encrypt(Application): header = "File encryption utility using the SSLeay ciphers" about = """\ Encrypts or decrypts the files given on the command line. If no options are given the filenames extensions are taken as hint: '.enc' means encrypted, everything else not encrypted. The utility then goes and switches the state of the files. Overwriting of files only takes place in case the '-O' switch is set. The following ciphers are supported: RC2, RC4, RC5, IDEA, Blowfish, DES, DES3, CAST This tool comes with NO WARRANTY. Use at YOUR OWN RISK. It may destroy data ! There is NO way to recover a forgotten pass phrase ! """ options = [SwitchOption('-e', 'encrypt'), SwitchOption('-d', 'decyrpt'), SwitchOption('-a', 'use the same key for all files'), SwitchOption('-O', 'allow overwrites (use with care)'), ArgumentOption('-c', 'cipher to use', 'RC5'), ] def main(self): overwrite = self.values['-O'] ciphername = self.values['-c'] samekey = self.values['-a'] for file in self.files: if not samekey: reset_key() print '-'*78 print 'Working on file:',file try: if self.values['-e']: encrypt(file,ciphername,overwrite) elif self.values['-d']: decrypt(file,overwrite) elif file[-4:] != '.enc': encrypt(file,ciphername,overwrite) else: decrypt(file,overwrite) except OperationalError,why: print '%s skipped -- %s' % (file,why) except IOError,(code,why): print '%s skipped -- %s' % (file,why) except os.error,why: print '%s skipped -- %s' % (file,why) except KeyboardInterrupt: print '*user break*' break if __name__ == '__main__': Encrypt()