Add phnxdeco with debian patch set (version 0.33-3).
[delutions.git] / tc / python / truecrypt5.py
blobcd30669846f7cc34da1c4f0151daffc57cad24db
1 ## truecrypt5.py - partial TrueCrypt 5 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.
27 ## Feb 13 2008: Added TrueCrypt 5 volume support.
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 xts import *
42 from keystrengthening5 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 Cascades = [
83 [Rijndael],
84 [Serpent],
85 [Twofish],
86 [Twofish, Rijndael],
87 [Serpent, Twofish, Rijndael],
88 [Rijndael, Serpent],
89 [Rijndael, Twofish, Serpent],
90 [Serpent, Twofish]
94 # TrueCrypt
97 TC_SECTOR_SIZE = 512
98 TC_HIDDEN_VOLUME_OFFSET = 1536
100 def Decrypt(ciphers, i, n, ciphertext):
101 assert len(ciphertext) == 16
102 for cipher1, cipher2 in reversed(ciphers):
103 ciphertext = XTSDecrypt(cipher1, cipher2, i, n, ciphertext)
104 return ciphertext
106 def DecryptMany(ciphers, n, blocks):
107 length = len(blocks)
108 assert length % 16 == 0
109 data = ''
110 for i in xrange(length / 16):
111 data += Decrypt(ciphers, i, n, blocks[0:16])
112 blocks = blocks[16:]
113 return data
115 class TrueCryptVolume5:
116 """Object representing a TrueCrypt 5 volume."""
117 def __init__(self, fileobj, password, progresscallback=lambda x: None):
119 self.fileobj = fileobj
120 self.decrypted_header = None
121 self.cipher = None
122 self.hidden_size = 0
124 for volume_type in ["normal", "hidden"]:
125 fileobj.seek(0)
126 if volume_type == "hidden":
127 fileobj.seek(-TC_HIDDEN_VOLUME_OFFSET, 2)
129 progresscallback("Is this a " + volume_type + " volume?")
131 salt = fileobj.read(64)
132 header = fileobj.read(448)
134 assert len(salt) == 64
135 assert len(header) == 448
137 HMACs = [
138 (HMAC_SHA512, 1000, "SHA-512"),
139 (HMAC_RIPEMD160, 2000, "RIPEMD-160"),
140 (HMAC_WHIRLPOOL, 1000, "Whirlpool")
142 for hmac, iterations, hmac_name in HMACs:
143 progresscallback("Trying " + hmac_name)
144 header_keypool = PBKDF2(hmac, password, salt, iterations, 32*6)
146 # Key strengthening done. Try all cipher algorithm combos.
147 for cascade in Cascades:
148 cipherlist1, cipherlist2 = [], []
150 if len(cascade) == 1:
151 key1a = header_keypool[0:32]
153 key1b = header_keypool[32:64]
155 algo1, = cascade
156 cipherlist1 = [ algo1(key1a) ]
157 cipherlist2 = [ algo1(key1b) ]
158 elif len(cascade) == 2:
159 key1a = header_keypool[0:32]
160 key2a = header_keypool[32:64]
162 key1b = header_keypool[64:96]
163 key2b = header_keypool[96:128]
165 algo1, algo2 = cascade
166 cipherlist1 = [ algo1(key1a), algo2(key2a) ]
167 cipherlist2 = [ algo1(key1b), algo2(key2b) ]
168 elif len(cascade) == 3:
169 key1a = header_keypool[0:32]
170 key2a = header_keypool[32:64]
171 key3a = header_keypool[64:96]
173 key1b = header_keypool[96:128]
174 key2b = header_keypool[128:160]
175 key3b = header_keypool[160:192]
177 algo1, algo2, algo3 = cascade
178 cipherlist1 = [ algo1(key1a), algo2(key2a), algo3(key3a) ]
179 cipherlist2 = [ algo1(key1b), algo2(key2b), algo3(key3b) ]
181 self.cipherlist = zip(cipherlist1, cipherlist2)
183 progresscallback("..." + str([ciph.get_name() for ciph in cipherlist1]) )
185 decrypted_header = DecryptMany(self.cipherlist, 0, header)
186 if TCIsValidVolumeHeader(decrypted_header):
187 # Success.
188 self.decrypted_header = decrypted_header
189 master_keypool = decrypted_header[192:]
191 cipherlist1, cipherlist2 = [], []
193 if len(cascade) == 1:
194 key1a = master_keypool[0:32]
196 key1b = master_keypool[32:64]
198 algo1, = cascade
199 cipherlist1 = [ algo1(key1a) ]
200 cipherlist2 = [ algo1(key1b) ]
201 elif len(cascade) == 2:
202 key1a = master_keypool[0:32]
203 key2a = master_keypool[32:64]
205 key1b = master_keypool[64:96]
206 key2b = master_keypool[96:128]
208 algo1, algo2 = cascade
209 cipherlist1 = [ algo1(key1a), algo2(key2a) ]
210 cipherlist2 = [ algo1(key1b), algo2(key2b) ]
211 elif len(cascade) == 3:
212 key1a = master_keypool[0:32]
213 key2a = master_keypool[32:64]
214 key3a = master_keypool[64:96]
216 key1b = master_keypool[96:128]
217 key2b = master_keypool[128:160]
218 key3b = master_keypool[160:192]
220 algo1, algo2, algo3 = cascade
221 cipherlist1 = [ algo1(key1a), algo2(key2a), algo3(key3a) ]
222 cipherlist2 = [ algo1(key1b), algo2(key2b), algo3(key3b) ]
224 self.cipherlist = zip(cipherlist1, cipherlist2)
226 self.hidden_size = BE64(decrypted_header[28:28+8])
228 progresscallback("Success!")
229 return
230 # Failed attempt.
231 raise KeyError, "incorrect password (or not a truecrypt volume)"
233 def __repr__(self):
234 if not self.decrypted_header:
235 return "<TrueCryptVolume5>"
236 return "<TrueCryptVolume5 %s %s>" % (self.cipher1.get_name(), self.info_hash)
238 def TCIsValidVolumeHeader(header):
239 magic = header[0:4]
240 checksum = BE32(header[8:12])
241 return magic == 'TRUE' and CRC32(header[192:448]) == checksum
243 def TCReadSector(tc, index):
244 """Read a sector from the volume."""
245 assert index > 0
246 tc.fileobj.seek(0, 2)
247 file_len = tc.fileobj.tell()
249 # For a regular (non-hidden) volume the file system starts at byte
250 # 512. However for a hidden volume, the start of the file system
251 # is not at byte 512. Starting from the end of the volume, namely
252 # byte file_len, we subtract the hidden volume salt+header (at offset
253 # 1536 from the end of the file). We then subtract the size of the
254 # hidden volume.
255 mod = 0
256 last_sector_offset = TC_SECTOR_SIZE
257 if tc.hidden_size:
258 mod = file_len - tc.hidden_size - TC_HIDDEN_VOLUME_OFFSET
259 # We subtract another sector from mod because the index starts
260 # at 1 and not 0.
261 mod -= TC_SECTOR_SIZE
262 last_sector_offset = TC_SECTOR_SIZE + TC_HIDDEN_VOLUME_OFFSET
263 seekto = mod + TC_SECTOR_SIZE * index
265 # last_sector_offset is the beginning of the last sector relative
266 # the end of the file. For a regular non-hidden volume this is simply
267 # 512 bytes from the end of the file. However for hidden volumes we
268 # must not read past the headers, so the last sector begins 512 bytes
269 # before the header offset.
270 if seekto > file_len - last_sector_offset:
271 return ''
273 tc.fileobj.seek(seekto)
274 data = tc.fileobj.read(TC_SECTOR_SIZE)
276 # In TrueCrypt 5 the dataunit index is always a function of the
277 # offset in the volume file, even for hidden volumes. This means
278 # the first dataunit index for hidden volumes is not 1. For
279 # regular volumes, mod/512 will be 0. For hidden volumes mod/512
280 # is the dataunit index of the first sector, minus 1
281 # (so mod/512 + 1 is the first dataunit).
282 return DecryptMany(tc.cipherlist, mod/512 + index, data)
285 def TCSectorCount(tc):
286 """How many sectors can we read with TCReadSector?"""
287 volume_size = 0
288 if tc.hidden_size:
289 volume_size = tc.hidden_size
290 else:
291 tc.fileobj.seek(0, 2)
292 volume_size = tc.fileobj.tell()
293 # Minus the salt+header.
294 volume_size -= 512
295 return volume_size / TC_SECTOR_SIZE
297 def cmdline():
298 scriptname = sys.argv[0]
299 try:
300 path, password, outfile = sys.argv[1:]
301 except ValueError:
302 print >> sys.stderr, "%s volumepath password outfile" % scriptname
303 sys.exit(1)
305 if os.path.exists(outfile):
306 print >> sys.stderr, "outfile %s already exists. use another " \
307 "filename and try again (we don't want to overwrite " \
308 "files by mistake)" % outfile
309 sys.exit(1)
311 try:
312 fileobj = file(path, "rb")
313 except IOError:
314 print >> sys.stderr, "file %s doesn't exist" % path
315 sys.exit(1)
317 tc = None
318 try:
319 tc = TrueCryptVolume5(fileobj, password, Log)
320 except KeyError:
321 print >> sys.stderr, "incorrect password or not a TrueCrypt volume"
322 fileobj.close()
323 sys.exit(1)
324 except KeyboardInterrupt:
325 print >> sys.stderr, "aborting"
326 fileobj.close()
327 sys.exit(1)
329 outfileobj = file(outfile, "ab")
330 num_sectors = TCSectorCount(tc)
331 num_written = 0
332 try:
333 for i in xrange(1, num_sectors + 1):
334 if i % 100 == 0:
335 Log("Decrypting sector %d of %d." % (i, num_sectors))
336 outfileobj.write(TCReadSector(tc, i))
337 num_written += 1
338 except KeyboardInterrupt:
339 print "Aborted decryption."
340 pass
341 outfileobj.close()
342 print "Wrote %d sectors (%d bytes)." % (num_written,
343 num_written * TC_SECTOR_SIZE)
344 fileobj.close()
345 sys.exit(0)
347 if __name__ == '__main__':
348 cmdline()
349 sys.exit(0)
351 ## If you want to use the code from the toploop:
353 ## fileobj = file("volume.tc", "rb")
354 ## tc = TrueCryptVolume5(fileobj, "password", Log)