]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - Crypto/Demo/enc.py
allmydata.Crypto: fix all internal imports
[tahoe-lafs/tahoe-lafs.git] / Crypto / Demo / enc.py
1 #!/usr/local/bin/python -Ou
2
3 """ enc - encrypt/decrypt files using one of SSLeay's ciphers.
4
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.
8
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 !
12 """
13 import exceptions,os,string,time,sys
14 from allmydata.CryptoWorld import Ciphers,Hashes,Utils
15 from CommandLine import Application,SwitchOption,ArgumentOption
16
17 # Globals
18 verbose = 0
19
20 # Maximum block size used for en/decryption
21 MAX_BLOCKSIZE = 1024 * 1000
22
23 class OperationalError(exceptions.StandardError):
24     pass
25
26 def filesize(file):
27
28     oldpos = file.tell()
29     file.seek(0,2)
30     size = file.tell()
31     file.seek(oldpos)
32     return size
33
34 def invisible_input(prompt='>>> '):
35
36     """ Adapted from the Python 1.5.1 docs example getpass()
37     """
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
43     try:
44         termios.tcsetattr(fd, TERMIOS.TCSADRAIN, new)
45         passwd = raw_input(prompt)
46     finally:
47         termios.tcsetattr(fd, TERMIOS.TCSADRAIN, old)
48     print
49     return passwd
50
51 def tempfile(filename='tmp',
52
53              maxint=sys.maxint,time=time.time,int=int,hex=hex,
54              exists=os.path.exists):
55
56     """ Return a new filename for a temporary file (based on filename).
57     """
58     temp = filename + '.' + hex(maxint % int(time())) + '.tmp'
59     if not exists(temp):
60         return temp
61     # Ok, find an alternative name
62     i = 0
63     while 1:
64         temp = '%s.%s-%i.tmp' % (filename,hex(maxint % int(time())),i)
65         if not exists(temp):
66             return temp
67         i = i + 1
68
69 # Global key
70 _key = ''
71
72 def get_cipher(name,check=0):
73
74     global _key
75
76     cc = getattr(Ciphers,name)
77     keysize = cc.keysize
78     if not _key:
79         while 1:
80             key1 = invisible_input('Please enter the key phrase: ')
81             if check:
82                 key2 = invisible_input('Please reenter the phrase: ')
83                 if key1 != key2:
84                     print "Phrases don't match. Please start again..."
85                     continue
86             if len(key1) == 0:
87                 print "Empty key phrase. Please start again..."
88             else:
89                 break
90         _key = key1
91     key = _key
92     # Fit key
93     if keysize > 0:
94         if len(key) < keysize:
95             key = key + \
96                   'Do not change this string, it is important !'\
97                   [:keysize - len(key)]
98         elif len(key) > keysize:
99             key = key[:keysize]
100     cipher = cc(key,Ciphers.CBC)
101     return cipher
102
103 def reset_key():
104
105     global _key
106
107     _key = ''
108
109 ###
110
111 def encrypt(filename,ciphername,overwrite=0):
112
113     if verbose:
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'
119
120     # Check overwrites
121     if os.path.exists(filename + '.enc'):
122         if not overwrite:
123             raise OperationalError,'would overwrite an existing file'
124         elif os.path.samefile(filename, filename + '.enc'):
125             raise OperationalError,'would overwrite the original file'
126
127     # Open plain file
128     f = open(filename,'rb')
129     size = filesize(f)
130     if verbose:
131         print  ' total size: %i bytes' % size
132
133     # Open work file
134     workfilename = tempfile(filename)
135     out = open(workfilename,'wb')
136
137     try:
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))
142
143         # Init hash and blocksize
144         hash = Hashes.MD5()
145         blocksize = size
146         if blocksize > MAX_BLOCKSIZE:
147             blocksize = MAX_BLOCKSIZE
148         blocksize = ((blocksize + cipher.blocksize - 1) / cipher.blocksize) \
149                     * cipher.blocksize
150
151         # Write the encrypted data in blocks
152         bytesread = 0
153         while bytesread < size:
154             if verbose:
155                 print  ' reading %i bytes...' % blocksize,
156             block = f.read(blocksize)
157             if verbose:
158                 print  'read %i bytes' % len(block)
159             bytesread = bytesread + len(block)
160             hash.update(block)
161             if bytesread == size:
162                 # Final block
163                 offset = len(block) % cipher.blocksize
164                 if offset:
165                     padsize = cipher.blocksize - offset
166                     block = block + '\0'*padsize
167                     if verbose:
168                         print  ' padding with %i bytes' % (padsize)
169             encblock = cipher.encrypt(block)
170             out.write(encblock)
171
172         # Write hash value
173         hash_value = hash.digest()
174         if verbose:
175             print ' hash value:',repr(hash_value)
176         out.write(hash_value)
177
178         # Copy work file to .enc file
179         out.close()
180         f.close()
181         os.rename(workfilename,filename+'.enc')
182         workfilename = None
183
184     finally:
185         if workfilename:
186             if not out.closed:
187                 out.close()
188             os.remove(workfilename)
189
190 ###
191
192 def decrypt(filename,overwrite=0):
193
194     if verbose:
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'
200
201     # Read header from cipher file
202     f = open(filename,'rb')
203     header = string.split(f.readline())
204     if len(header) != 4:
205         raise OperationalError,'wrong header format:'+ str(header)
206     origfilename = eval(header[1])
207     ciphername = header[2]
208     size = string.atoi(header[3])
209     if verbose:
210         print  ' total size: %i bytes' % size
211
212     # Check overwrites
213     if os.path.exists(origfilename):
214         if not overwrite:
215             raise OperationalError,'would overwrite an existing file'
216         elif os.path.samefile(origfilename, filename):
217             raise OperationalError,'would overwrite the encrypted file'
218
219     # Open work file
220     workfilename = tempfile(filename)
221     out = open(workfilename,'wb')
222
223     try:
224
225         # Load cipher and init hash
226         cipher = get_cipher(ciphername)
227         hash = Hashes.MD5()
228
229         # Read the encrypted data in blocks
230         blocksize = size
231         if blocksize > MAX_BLOCKSIZE:
232             blocksize = MAX_BLOCKSIZE
233         blocksize = ((blocksize + cipher.blocksize - 1) / cipher.blocksize) \
234                     * cipher.blocksize
235         bytesread = 0
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
242             if verbose:
243                 print  ' reading %i bytes...' % blocksize,
244             encblock = f.read(blocksize)
245             if verbose:
246                 print 'read %i bytes' % len(encblock)
247             bytesread = bytesread + len(encblock)
248             block = cipher.decrypt(encblock)
249             if bytesread > size:
250                 # Depad
251                 padsize = bytesread - size
252                 block = block[:-padsize]
253                 if verbose:
254                     print ' depadded last block by %i bytes' % (padsize)
255             hash.update(block)
256             out.write(block)
257
258         # Check hash value
259         hash_value = f.read(hash.digestsize)
260         if verbose:
261             print ' hash value:',repr(hash_value)
262         if hash_value != hash.digest():
263             raise OperationalError,'data corrupt'
264
265         # Copy workfile to origfile
266         out.close()
267         f.close()
268         os.rename(workfilename,origfilename)
269         workfilename = None
270
271     finally:
272         if workfilename:
273             if not out.closed:
274                 out.close()
275             os.remove(workfilename)
276
277 ###
278
279 class Encrypt(Application):
280
281     header = "File encryption utility using the SSLeay ciphers"
282
283     about = """\
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.
289
290 The following ciphers are supported:
291       RC2, RC4, RC5, IDEA, Blowfish, DES, DES3, CAST
292
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 !
295 """
296
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'),
302                ]
303
304     def main(self):
305
306         overwrite = self.values['-O']
307         ciphername = self.values['-c']
308         samekey = self.values['-a']
309         for file in self.files:
310             if not samekey:
311                 reset_key()
312                 print '-'*78
313                 print 'Working on file:',file
314             try:
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)
321                 else:
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)
327             except os.error,why:
328                 print '%s skipped -- %s' % (file,why)
329             except KeyboardInterrupt:
330                 print '*user break*'
331                 break
332
333 if __name__ == '__main__':
334     Encrypt()