1 #!/usr/local/bin/python -Ou
3 """ enc - encrypt/decrypt files using one of SSLeay's ciphers.
5 Copyright (c) 1998 by Marc-Andre Lemburg; All Rights Reserved;
6 mailto:mal@lemburg.com; See the documentation for further
7 copyright information or contact the author.
9 DISCLAIMER & WARNING: This tool comes with NO WARRANTY. Use at
10 YOUR OWN RISK. It may destroy data ! There is NO way to recover a
11 forgotten pass phrase !
13 import exceptions,os,string,time,sys
14 from allmydata.CryptoWorld import Ciphers,Hashes,Utils
15 from CommandLine import Application,SwitchOption,ArgumentOption
20 # Maximum block size used for en/decryption
21 MAX_BLOCKSIZE = 1024 * 1000
23 class OperationalError(exceptions.StandardError):
34 def invisible_input(prompt='>>> '):
36 """ Adapted from the Python 1.5.1 docs example getpass()
38 import termios,TERMIOS
39 fd = sys.stdin.fileno()
40 old = termios.tcgetattr(fd)
41 new = termios.tcgetattr(fd)
42 new[3] = new[3] & ~TERMIOS.ECHO # fix lflags
44 termios.tcsetattr(fd, TERMIOS.TCSADRAIN, new)
45 passwd = raw_input(prompt)
47 termios.tcsetattr(fd, TERMIOS.TCSADRAIN, old)
51 def tempfile(filename='tmp',
53 maxint=sys.maxint,time=time.time,int=int,hex=hex,
54 exists=os.path.exists):
56 """ Return a new filename for a temporary file (based on filename).
58 temp = filename + '.' + hex(maxint % int(time())) + '.tmp'
61 # Ok, find an alternative name
64 temp = '%s.%s-%i.tmp' % (filename,hex(maxint % int(time())),i)
72 def get_cipher(name,check=0):
76 cc = getattr(Ciphers,name)
80 key1 = invisible_input('Please enter the key phrase: ')
82 key2 = invisible_input('Please reenter the phrase: ')
84 print "Phrases don't match. Please start again..."
87 print "Empty key phrase. Please start again..."
94 if len(key) < keysize:
96 'Do not change this string, it is important !'\
98 elif len(key) > keysize:
100 cipher = cc(key,Ciphers.CBC)
111 def encrypt(filename,ciphername,overwrite=0):
114 print 'Encrypting:',filename
115 if filename[-4:] == '.enc':
116 raise OperationalError,'already encrypted'
117 if not os.path.isfile(filename):
118 raise OperationalError,'not a file or not found'
121 if os.path.exists(filename + '.enc'):
123 raise OperationalError,'would overwrite an existing file'
124 elif os.path.samefile(filename, filename + '.enc'):
125 raise OperationalError,'would overwrite the original file'
128 f = open(filename,'rb')
131 print ' total size: %i bytes' % size
134 workfilename = tempfile(filename)
135 out = open(workfilename,'wb')
138 # Init cipher and write header
139 cipher = get_cipher(ciphername,check=1)
140 out.write('enc %s %s %i\n' % \
141 (repr(filename),ciphername,size))
143 # Init hash and blocksize
146 if blocksize > MAX_BLOCKSIZE:
147 blocksize = MAX_BLOCKSIZE
148 blocksize = ((blocksize + cipher.blocksize - 1) / cipher.blocksize) \
151 # Write the encrypted data in blocks
153 while bytesread < size:
155 print ' reading %i bytes...' % blocksize,
156 block = f.read(blocksize)
158 print 'read %i bytes' % len(block)
159 bytesread = bytesread + len(block)
161 if bytesread == size:
163 offset = len(block) % cipher.blocksize
165 padsize = cipher.blocksize - offset
166 block = block + '\0'*padsize
168 print ' padding with %i bytes' % (padsize)
169 encblock = cipher.encrypt(block)
173 hash_value = hash.digest()
175 print ' hash value:',repr(hash_value)
176 out.write(hash_value)
178 # Copy work file to .enc file
181 os.rename(workfilename,filename+'.enc')
188 os.remove(workfilename)
192 def decrypt(filename,overwrite=0):
195 print 'Decrypting:',filename
196 if filename[-4:] != '.enc':
197 raise OperationalError,'decrypt a plain file'
198 if not os.path.isfile(filename):
199 raise OperationalError,'not a file or not found'
201 # Read header from cipher file
202 f = open(filename,'rb')
203 header = string.split(f.readline())
205 raise OperationalError,'wrong header format:'+ str(header)
206 origfilename = eval(header[1])
207 ciphername = header[2]
208 size = string.atoi(header[3])
210 print ' total size: %i bytes' % size
213 if os.path.exists(origfilename):
215 raise OperationalError,'would overwrite an existing file'
216 elif os.path.samefile(origfilename, filename):
217 raise OperationalError,'would overwrite the encrypted file'
220 workfilename = tempfile(filename)
221 out = open(workfilename,'wb')
225 # Load cipher and init hash
226 cipher = get_cipher(ciphername)
229 # Read the encrypted data in blocks
231 if blocksize > MAX_BLOCKSIZE:
232 blocksize = MAX_BLOCKSIZE
233 blocksize = ((blocksize + cipher.blocksize - 1) / cipher.blocksize) \
236 while bytesread < size:
237 if size - bytesread < blocksize:
238 # Read remaining data only
239 blocksize = size - bytesread
240 blocksize = ((blocksize + cipher.blocksize - 1) / \
241 cipher.blocksize) * cipher.blocksize
243 print ' reading %i bytes...' % blocksize,
244 encblock = f.read(blocksize)
246 print 'read %i bytes' % len(encblock)
247 bytesread = bytesread + len(encblock)
248 block = cipher.decrypt(encblock)
251 padsize = bytesread - size
252 block = block[:-padsize]
254 print ' depadded last block by %i bytes' % (padsize)
259 hash_value = f.read(hash.digestsize)
261 print ' hash value:',repr(hash_value)
262 if hash_value != hash.digest():
263 raise OperationalError,'data corrupt'
265 # Copy workfile to origfile
268 os.rename(workfilename,origfilename)
275 os.remove(workfilename)
279 class Encrypt(Application):
281 header = "File encryption utility using the SSLeay ciphers"
284 Encrypts or decrypts the files given on the command line. If no
285 options are given the filenames extensions are taken as hint: '.enc'
286 means encrypted, everything else not encrypted. The utility then goes
287 and switches the state of the files. Overwriting of files only takes
288 place in case the '-O' switch is set.
290 The following ciphers are supported:
291 RC2, RC4, RC5, IDEA, Blowfish, DES, DES3, CAST
293 This tool comes with NO WARRANTY. Use at YOUR OWN RISK. It may destroy
294 data ! There is NO way to recover a forgotten pass phrase !
297 options = [SwitchOption('-e', 'encrypt'),
298 SwitchOption('-d', 'decyrpt'),
299 SwitchOption('-a', 'use the same key for all files'),
300 SwitchOption('-O', 'allow overwrites (use with care)'),
301 ArgumentOption('-c', 'cipher to use', 'RC5'),
306 overwrite = self.values['-O']
307 ciphername = self.values['-c']
308 samekey = self.values['-a']
309 for file in self.files:
313 print 'Working on file:',file
315 if self.values['-e']:
316 encrypt(file,ciphername,overwrite)
317 elif self.values['-d']:
318 decrypt(file,overwrite)
319 elif file[-4:] != '.enc':
320 encrypt(file,ciphername,overwrite)
322 decrypt(file,overwrite)
323 except OperationalError,why:
324 print '%s skipped -- %s' % (file,why)
325 except IOError,(code,why):
326 print '%s skipped -- %s' % (file,why)
328 print '%s skipped -- %s' % (file,why)
329 except KeyboardInterrupt:
333 if __name__ == '__main__':