Monday, April 4, 2011

If you forgot your TrueCrypt passphrase but can remember the mnemonic...

A number of months ago this happened to me. Then I wrote this script, named it johnny.py, let it run overnight, and it found my passphrase. (BTW its name is inspired by John The Ripper; I'm not much of a Keanu fan). Feed it the class of all possible passwords that might correspond to your passphrase in the form of a space-delineated list, based on your password's mnemonic (each list a class of possible characters, i.e. aA@4 for the letter "A"), and it will iterate over every permutation.

For example:
$ ./johnny.py truecrypt_container Bb Mm '5$Ss' Mm 'A@4a'

will try all possible L337-speak translations of the first letter of each word in the phrase "Bite My Shiny Metal Ass" for the truecrypt file container "truecrypt_container". I believe you can also use the script on a truecrypted hard drive partition, if applicable (by passing the path to the specific block device), though I haven't tested that using it in this way works.

Please note that I wrote it for Linux, and if any part of the keyspace contains metacharacters, you may need to use single-quotes in passing that part of it. Also, the account on your machine needs to be sudo-able (well of course; you're running TrueCrypt!)
#!/usr/bin/env python

import re,subprocess,sys
from os import path
from functools import reduce

def callTC(keyattempt):
global TCloc,volume
comm = ['sudo',TCloc,'--text','--non-interactive',"--password="+keyattempt,volume]
return subprocess.Popen(comm,stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()

def tryKey(position):
global keyspace,keylen,nptot,permutation,done,nperm,passphrase,volume
# If not at the bottom level of recursion yet, assemble the trial password
if(position < keylen):
for char in keyspace[position]:
permutation[position] = char
trial=tryKey(position+1)
if(trial):
return True
return False
# If at the bottom level, the permutation has been completed, so attempt to run the command
else:
nperm += 1 # one more permutation is being tried
passphrase=''.join(permutation)
trial = callTC(passphrase)[1]
if ( trial.decode('utf-8').rfind('already mounted') != -1) :
print ('(unmounting volume...)')
subprocess.call(['sudo','truecrypt','-d',volume])
nperm -= 1;
return tryKey(position)
if((done+1.0)/100.0 <= nperm*1.0/nptot):
done = int(100*float(nperm)/float(nptot))
print("%3d%% (%d of %d) total permutations tried; current guess = %s" % (done,nperm,nptot,passphrase))
return ( trial.decode('utf-8') == '' ) # stderr

def main():
global TCloc,volume,keyspace,keylen,nptot,permutation,passphrase,done,nperm
if(len(sys.argv) < 3):
print "Usage: johnny.py [path to TrueCrypt volume] [set of possible first characters] [set of possible second characters] ...\n"
print "Recover a TrueCrypt password based on a rememberd mnemonic. Pass johnny.py a space-delineated list of strings each"
print "representing the set of possible characters for each character in the password. If your passphrase contains a space, use quotes ;-)"
sys.exit(0)
TCloc = ((subprocess.Popen(['which','truecrypt'],stdout=subprocess.PIPE)).communicate())[0][0:-1]
volume=sys.argv[1]
keyspace = sys.argv[2:]
keylen = len(keyspace)
nptot = reduce(lambda x, y: x*y,[len(chars) for chars in keyspace])
permutation = ['' for x in keyspace]
done = 0
nperm = 0


if (not path.exists(path.dirname(volume))):
print('File ' + volume + ': no such file or directory.')
elif (not path.exists(volume)):
print('File ' + volume + ': not found in ' + path.dirname(volume))
else:
passfile = open('pass.txt','w')
print("Trying each of the %d possible keys..." % nptot)
if(tryKey(0)):
print('Successfully determined TrueCrypt password: ' + passphrase)
passfile.write(passphrase)
passfile.close()
else:
print('Did not succeed in finding TrueCrypt password in specified keyspace.')

if __name__ == '__main__':
main()


I know, this may be reinventing the wheel, but since I couldn't find something like this myself I decided to cobble it together so that I could use it myself, and only recently I remembered that I had it. I have neglected this blog for ages, and though I'm sharing it just in case it would be useful to someone who Googles for an answer in the future, it's really just the perfect excuse to start posting.

No comments:

Post a Comment