1 ## truecrypt5.py - partial TrueCrypt 5 implementation in Python.
2 ## Copyright (c) 2008 Bjorn Edstrom <be@bjrn.se>
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
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.
26 ## Jan 4 2008: Initial version. Plenty of room for improvements.
27 ## Feb 13 2008: Added TrueCrypt 5 volume support.
38 from rijndael
import Rijndael
39 from serpent
import Serpent
40 from twofish
import Twofish
42 from keystrengthening5
import *
53 print >> sys
.stderr
, "Progress:", message
57 crc
= binascii
.crc32(data
)
58 # Convert from signed to unsigned word32.
59 return crc
% 0x100000000
62 """Bytes to 16 bit big endian word."""
63 return struct
.unpack(">H", x
)[0]
66 """Bytes to 32 bit big endian word."""
67 return struct
.unpack(">L", x
)[0]
70 """Bytes to 64 bit big endian word."""
71 a
, b
= struct
.unpack(">LL", x
)
74 def Win32FileTime2UnixTime(filetime
):
75 """Converts a win32 FILETIME to a unix timestamp."""
76 return filetime
/ 10000000 - 11644473600
87 [Serpent
, Twofish
, Rijndael
],
89 [Rijndael
, Twofish
, Serpent
],
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
)
106 def DecryptMany(ciphers
, n
, blocks
):
108 assert length
% 16 == 0
110 for i
in xrange(length
/ 16):
111 data
+= Decrypt(ciphers
, i
, n
, blocks
[0:16])
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
124 for volume_type
in ["normal", "hidden"]:
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
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]
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
):
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]
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!")
231 raise KeyError, "incorrect password (or not a truecrypt volume)"
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
):
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."""
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
256 last_sector_offset
= TC_SECTOR_SIZE
258 mod
= file_len
- tc
.hidden_size
- TC_HIDDEN_VOLUME_OFFSET
259 # We subtract another sector from mod because the index starts
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
:
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?"""
289 volume_size
= tc
.hidden_size
291 tc
.fileobj
.seek(0, 2)
292 volume_size
= tc
.fileobj
.tell()
293 # Minus the salt+header.
295 return volume_size
/ TC_SECTOR_SIZE
298 scriptname
= sys
.argv
[0]
300 path
, password
, outfile
= sys
.argv
[1:]
302 print >> sys
.stderr
, "%s volumepath password outfile" % scriptname
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
312 fileobj
= file(path
, "rb")
314 print >> sys
.stderr
, "file %s doesn't exist" % path
319 tc
= TrueCryptVolume5(fileobj
, password
, Log
)
321 print >> sys
.stderr
, "incorrect password or not a TrueCrypt volume"
324 except KeyboardInterrupt:
325 print >> sys
.stderr
, "aborting"
329 outfileobj
= file(outfile
, "ab")
330 num_sectors
= TCSectorCount(tc
)
333 for i
in xrange(1, num_sectors
+ 1):
335 Log("Decrypting sector %d of %d." % (i
, num_sectors
))
336 outfileobj
.write(TCReadSector(tc
, i
))
338 except KeyboardInterrupt:
339 print "Aborted decryption."
342 print "Wrote %d sectors (%d bytes)." % (num_written
,
343 num_written
* TC_SECTOR_SIZE
)
347 if __name__
== '__main__':
351 ## If you want to use the code from the toploop:
353 ## fileobj = file("volume.tc", "rb")
354 ## tc = TrueCryptVolume5(fileobj, "password", Log)