Add phnxdeco with debian patch set (version 0.33-3).
[delutions.git] / tc / python / truecrypt.py
blob7e186456a0b785da1fcf6e502ccd2a2ab418b357
1 ## truecrypt.py - partial TrueCrypt implementation in Python.
2 ## Copyright (c) 2008 Bjorn Edstrom <be@bjrn.se>
3 ##
4 ## Permission is hereby granted, free of charge, to any person
5 ## obtaining a copy of this software and associated documentation
6 ## files (the "Software"), to deal in the Software without
7 ## restriction, including without limitation the rights to use,
8 ## copy, modify, merge, publish, distribute, sublicense, and/or sell
9 ## copies of the Software, and to permit persons to whom the
10 ## Software is furnished to do so, subject to the following
11 ## conditions:
13 ## The above copyright notice and this permission notice shall be
14 ## included in all copies or substantial portions of the Software.
16 ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 ## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 ## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 ## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 ## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 ## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 ## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 ## OTHER DEALINGS IN THE SOFTWARE.
24 ## --
25 ## Changelog
26 ## Jan 4 2008: Initial version. Plenty of room for improvements.
28 import struct
29 try:
30 import psyco
31 psyco.full()
32 except ImportError:
33 pass
35 import sys
36 import os
38 from rijndael import Rijndael
39 from serpent import Serpent
40 from twofish import Twofish
41 from lrw import *
42 from keystrengthening import *
45 # Utilities.
48 import struct
49 import time
50 import binascii
52 def Log(message):
53 print >> sys.stderr, "Progress:", message
55 def CRC32(data):
56 """Compute CRC-32."""
57 crc = binascii.crc32(data)
58 # Convert from signed to unsigned word32.
59 return crc % 0x100000000
61 def BE16(x):
62 """Bytes to 16 bit big endian word."""
63 return struct.unpack(">H", x)[0]
65 def BE32(x):
66 """Bytes to 32 bit big endian word."""
67 return struct.unpack(">L", x)[0]
69 def BE64(x):
70 """Bytes to 64 bit big endian word."""
71 a, b = struct.unpack(">LL", x)
72 return (a<<32) | b
74 def Win32FileTime2UnixTime(filetime):
75 """Converts a win32 FILETIME to a unix timestamp."""
76 return filetime / 10000000 - 11644473600
79 # Ciphers.
82 class CipherChain:
83 def __init__(self, ciphers):
84 self.ciphers = [ciph() for ciph in ciphers]
85 def set_key(self, keys):
86 i = 0
87 for cipher in self.ciphers:
88 cipher.set_key(keys[i])
89 i += 1
90 def encrypt(self, data):
91 for cipher in self.ciphers:
92 data = cipher.encrypt(data)
93 return data
94 def decrypt(self, data):
95 for cipher in reversed(self.ciphers):
96 data = cipher.decrypt(data)
97 return data
98 def get_name(self):
99 return '-'.join(reversed([cipher.get_name() for cipher in self.ciphers]))
101 Cascades = [
102 [Rijndael],
103 [Serpent],
104 [Twofish],
105 [Twofish, Rijndael],
106 [Serpent, Twofish, Rijndael],
107 [Rijndael, Serpent],
108 [Rijndael, Twofish, Serpent],
109 [Serpent, Twofish]
112 HMACs = [
113 (HMAC_SHA1, "SHA-1"),
114 (HMAC_RIPEMD160, "RIPEMD-160"),
115 (HMAC_WHIRLPOOL, "Whirlpool")
119 # TrueCrypt.
122 TC_SECTOR_SIZE = 512
123 TC_HIDDEN_VOLUME_OFFSET = 1536
125 class TrueCryptVolume:
126 """Object representing a TrueCrypt volume."""
127 def __init__(self, fileobj, password, progresscallback=lambda x: None):
129 self.fileobj = fileobj
130 self.decrypted_header = None
131 self.cipher = None
132 self.master_lrwkew = None
133 self.hidden_size = 0
135 for volume_type in ["normal", "hidden"]:
136 fileobj.seek(0)
137 if volume_type == "hidden":
138 fileobj.seek(-TC_HIDDEN_VOLUME_OFFSET, 2)
140 progresscallback("Is this a " + volume_type + " volume?")
142 salt = fileobj.read(64)
143 header = fileobj.read(448)
145 assert len(salt) == 64
146 assert len(header) == 448
148 for hmac, hmac_name in HMACs:
149 # Generate the keys needed to decrypt the volume header.
150 iterations = 2000
151 if hmac == HMAC_WHIRLPOOL:
152 iterations = 1000
154 info = ''
155 if hmac_name in "RIPEMD-160 Whirlpool":
156 info = ' (this will take a while)'
157 progresscallback("Trying " + hmac_name + info)
159 header_keypool = PBKDF2(hmac, password, salt, iterations, 128)
160 header_lrwkey = header_keypool[0:16]
161 header_key1 = header_keypool[32:64]
162 header_key2 = header_keypool[64:96]
163 header_key3 = header_keypool[96:128]
165 for cascade in Cascades:
166 # Try each cipher and cascades and see if we can successfully
167 # decrypt the header with it.
168 cipher = CipherChain(cascade)
170 progresscallback("..." + cipher.get_name())
172 cipher.set_key([header_key1, header_key2, header_key3])
173 decrypted_header = LRWMany(cipher.decrypt, header_lrwkey, 1, header)
174 if TCIsValidVolumeHeader(decrypted_header):
175 # Success.
176 self.decrypted_header = decrypted_header
178 master_keypool = decrypted_header[192:]
179 master_lrwkey = master_keypool[0:16]
180 master_key1 = master_keypool[32:64]
181 master_key2 = master_keypool[64:96]
182 master_key3 = master_keypool[96:128]
184 self.master_lrwkey = master_lrwkey
185 self.cipher = cipher
186 self.cipher.set_key([master_key1, master_key2, master_key3])
187 self.hidden_size = BE64(decrypted_header[28:28+8])
188 self.format_ver = BE16(decrypted_header[4:6])
190 # We don't really need the information below but we save
191 # it so it can be displayed by print_information()
192 self.info_hash = hmac_name
193 self.info_headerlrwkey = hexdigest(header_lrwkey)
194 self.info_headerkey = hexdigest(header_keypool[32:128])
195 self.info_masterkey = hexdigest(master_keypool[32:128])
197 progresscallback("Success!")
198 return
199 # Failed attempt.
200 raise KeyError, "incorrect password (or not a truecrypt volume)"
202 def __repr__(self):
203 if not self.decrypted_header:
204 return "<TrueCryptVolume>"
205 return "<TrueCryptVolume %s %s>" % (self.cipher.get_name(), self.info_hash)
207 def TCIsValidVolumeHeader(header):
208 magic = header[0:4]
209 checksum = BE32(header[8:12])
210 return magic == 'TRUE' and CRC32(header[192:448]) == checksum
212 def TCReadSector(tc, index):
213 """Read a sector from the volume."""
214 assert index > 0
215 tc.fileobj.seek(0, 2)
216 file_len = tc.fileobj.tell()
218 # The LRW functions work on blocks of length 16. Since a TrueCrypt
219 # sector is 512 bytes each call to LRWMany will decrypt 32 blocks,
220 # and each call to this function must therefore advance the block
221 # index 32. The block index also starts at 1, not 0. index 1
222 # corresponds to lrw_index 1, index 2 corresponds to lrw_index 33 etc.
223 lrw_index = (index - 1) * 32 + 1 # LRWSector2Index(index)
225 # For a regular (non-hidden) volume the file system starts at byte
226 # 512. However for a hidden volume, the start of the file system
227 # is not at byte 512. Starting from the end of the volume, namely
228 # byte file_len, we subtract the hidden volume salt+header (at offset
229 # 1536 from the end of the file). We then subtract the size of the
230 # hidden volume.
231 mod = 0
232 last_sector_offset = TC_SECTOR_SIZE
233 if tc.hidden_size:
234 mod = file_len - tc.hidden_size - TC_HIDDEN_VOLUME_OFFSET
235 # We subtract another sector from mod because the index starts
236 # at 1 and not 0.
237 mod -= TC_SECTOR_SIZE
238 last_sector_offset = TC_SECTOR_SIZE + TC_HIDDEN_VOLUME_OFFSET
239 seekto = mod + TC_SECTOR_SIZE * index
241 # last_sector_offset is the beginning of the last sector relative
242 # the end of the file. For a regular non-hidden volume this is simply
243 # 512 bytes from the end of the file. However for hidden volumes we
244 # must not read past the headers, so the last sector begins 512 bytes
245 # before the header offset.
246 if seekto > file_len - last_sector_offset:
247 return ''
249 tc.fileobj.seek(seekto)
250 data = tc.fileobj.read(TC_SECTOR_SIZE)
252 return LRWMany(tc.cipher.decrypt, tc.master_lrwkey, lrw_index, data)
254 def TCSectorCount(tc):
255 """How many sectors can we read with TCReadSector?"""
256 volume_size = 0
257 if tc.hidden_size:
258 volume_size = tc.hidden_size
259 else:
260 tc.fileobj.seek(0, 2)
261 volume_size = tc.fileobj.tell()
262 # Minus the salt+header.
263 volume_size -= 512
264 return volume_size / TC_SECTOR_SIZE
266 def TCPrintInformation(tc):
267 if not tc.decrypted_header:
268 return
270 header = tc.decrypted_header
271 program_ver = BE16(header[6:8])
272 volume_create = Win32FileTime2UnixTime(BE64(header[12:12+8]))
273 header_create = Win32FileTime2UnixTime(BE64(header[20:20+8]))
275 print "="*60
276 print "Raw Header"
277 print "="*60
278 print repr(tc.decrypted_header)
279 print "="*60
280 print "Parsed Header"
281 print "="*60
282 print "Hash :", tc.info_hash
283 print "Cipher :", tc.cipher.get_name()
284 if tc.hidden_size:
285 print "Volume Type : Hidden"
286 print "Hidden size :", tc.hidden_size
287 else:
288 print "Volume Type : Normal"
289 print "Header Key :", tc.info_headerkey
290 print "Header LRW Key:", tc.info_headerlrwkey
291 print "Master Key :", tc.info_masterkey
292 print "Master LRW Key:", hexdigest(tc.master_lrwkey)
293 print "Format ver :", hex(tc.format_ver)
294 print "Min prog. ver :", hex(program_ver)
295 print "Volume create :", time.asctime(time.localtime(volume_create))
296 print "Header create :", time.asctime(time.localtime(header_create))
297 print "="*60
299 def cmdline():
300 scriptname = sys.argv[0]
301 try:
302 path, password, outfile = sys.argv[1:]
303 except ValueError:
304 print >> sys.stderr, "%s volumepath password outfile" % scriptname
305 sys.exit(1)
307 if os.path.exists(outfile):
308 print >> sys.stderr, "outfile %s already exists. use another " \
309 "filename and try again (we don't want to overwrite " \
310 "files by mistake)" % outfile
311 sys.exit(1)
313 try:
314 fileobj = file(path, "rb")
315 except IOError:
316 print >> sys.stderr, "file %s doesn't exist" % path
317 sys.exit(1)
319 try:
320 tc = TrueCryptVolume(fileobj, password, Log)
321 except KeyError:
322 print >> sys.stderr, "incorrect password or not a TrueCrypt volume"
323 fileobj.close()
324 sys.exit(1)
325 except KeyboardInterrupt:
326 print >> sys.stderr, "aborting"
327 fileobj.close()
328 sys.exit(1)
330 TCPrintInformation(tc)
332 outfileobj = file(outfile, "ab")
333 num_sectors = TCSectorCount(tc)
334 num_written = 0
335 try:
336 for i in xrange(1, num_sectors + 1):
337 if i % 100 == 0:
338 Log("Decrypting sector %d of %d." % (i, num_sectors))
339 outfileobj.write(TCReadSector(tc, i))
340 num_written += 1
341 except KeyboardInterrupt:
342 print "Aborted decryption."
343 pass
344 outfileobj.close()
345 print "Wrote %d sectors (%d bytes)." % (num_written,
346 num_written * TC_SECTOR_SIZE)
347 fileobj.close()
348 sys.exit(0)
350 if __name__ == '__main__':
351 cmdline()
353 ## If you want to use the code from the toploop:
355 #fileobj = file("volume.tc", "rb")
356 #tc = TrueCryptVolume(fileobj, "password", Log)
357 #TCPrintInformation(tc)
358 #print repr(TCReadSector(tc, 1))
359 #print repr(TCReadSector(tc, 2))