Repository navigation
Expand file tree
/
Copy pathconvertKeePass2XmlToTrezorPassCsv.py
More file actions
executable file
·267 lines (227 loc) · 7.75 KB
/
Copy pathconvertKeePass2XmlToTrezorPassCsv.py
File metadata and controls
executable file
·267 lines (227 loc) · 7.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import sys
import os
import getopt
import xml.etree.ElementTree as ET
import csv
Version = u'1.1'
VersionDate = u'May 2017'
ifile = u'keepass2.xml'
ofile = u'trezorpass.csv'
def usage():
print('''convertKeePass2XmlToTrezorPassCsv.py [-v] [-h] [-i <keepass2.xml>] [-o <trezorpass.csv>]
-v, --version
print the version number
-h, --help
print help text
-i, --input
name of XML file generated from KeePass2 export
-o, --output
name of CSV file generated by this program which can then be imported into TrezorPass
All arguments are optional.
By default it expects the input file to be named "keepass2.xml".
By default it creates the output file named "trezorpass.csv". If it
already exists it will be overwritten.
The purpose of this program is to migrate a KeePass2 database to TrezorPass.
This is done in a 3-Step process.
Step 1: Open KeePass2, using the `File | Export` function in the menu, export
your .kdb file to an XML file of type `KeePass XML (2.x)`. Name the output file
`keepass2.xml`. KeePass2 also allows partial exports, like only exporting
your entries in the group `eMail`.
Step 2: Run this program `convertKeePass2XmlToTrezorPassCsv`. It will convert
the `keepass2.xml` file into a CSV file named `trezorpass.csv`. Be careful,
if that file exists, it will be overwritten. Optionally, if desired, you
can make manual changes to the `trezorpass.csv` file.
Step 3: Open TrezorPass and use the `Import plaintext CSV file`
function from the menu to import the `trezorpass.csv` file. All information
from the `trezorpass.csv` file will be added to any existing information
in the TrezorPass password database. Optionally, if desired, create an
empty TrezorPass password database first.
Examples:
# normal operation, expects file `keepass2.xml` to exist
convertKeePass2XmlToTrezorPassCsv.py
# operation with non-standard filenames
convertKeePass2XmlToTrezorPassCsv.py -i keepassEmail.xml -o trezorpassEmail.csv
Works in Python 2.7 and 3.4+.
Tested on Linux on Python 2.7.9 and 3.4.2.
Tested with KeePass v2.28.
''')
def printVersion():
"""
Show about and version information.
"""
print('Version: %s from %s' % (Version, VersionDate))
print('About convertKeePass2XmlToTrezorPassCsv: This program helps you \n'
' to migrate your KeePass v2 password database to TrezorPass.')
def parseArgs(argv):
global ifile, ofile
try:
opts, args = getopt.getopt(argv, 'vhi:o:',
['version', 'help', 'input=', 'output='])
except getopt.GetoptError as e:
print('Wrong arguments. Error: %s.', str(e))
sys.exit(2)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage()
sys.exit()
elif opt in ('-v', '--version'):
printVersion()
sys.exit()
elif opt in ('-i', '--input'):
ifile = arg
elif opt in ('-o', '--output'):
ofile = arg
if len(args) != 0:
print('Incorrect arguments %s found in command line. '
'Correct your input.' % str(args))
sys.exit(20)
if not os.path.isfile(ifile):
print('File "%s" does not exist, is not a proper file, '
'or is a directory. Aborting.' % ifile)
sys.exit(21)
if not os.access(ifile, os.R_OK):
print('File "%s" cannot be read. No read permissions. '
'Aborting.' % ifile)
sys.exit(22)
if os.path.isfile(ofile):
print('File "%s" already exist. It will be overwritten.' % ofile)
if not os.access(ofile, os.W_OK):
print('File "%s" cannot be written. No write permissions. '
'Aborting.' % ofile)
sys.exit(22)
def calculateDepth(member, currDepth):
max = currDepth
for submember in member.findall('Group'):
curr = calculateDepth(submember, currDepth+1)
if curr > max:
max = curr
return max
def printCsv(ofile):
with open(ofile, 'r') as f:
csv.register_dialect('escaped', doublequote=False, escapechar='\\')
reader = csv.reader(f, dialect='escaped')
for csvEntry in reader:
try:
print('CSV Entry: 0=%s, 1=%s, 2=%s, 3=%s' %
(csvEntry[0], csvEntry[1], csvEntry[2], csvEntry[3]))
except Exception as e:
print('ERROR: length of row = %d, row entry 0 = %s, Error text = %s' %
(len(csvEntry), csvEntry[0], e))
def escape(str):
"""
We need to escape \ as \\.
"""
if str is None or str == u'':
return u''
return str.replace('\\', '\\\\')
def writeEntriesToList(member, currDepth, currGroupName, max, entry_list, csvwriter):
# the root group in Keepass is the name of the .kdb file,
# this might be more molesting than useful. So, let us skip it.
skipRootName = True
for submember in member.findall('Group'):
mname = submember.find('Name').text # name of Group in XML
if skipRootName:
if currDepth <= 0:
mname = u''
if currGroupName != u'':
mname = u'--' + mname
writeEntriesToList(submember, currDepth+1, currGroupName + mname, max, entry_list, csvwriter)
for submember in member.findall('Entry'):
mydict = {}
for strmember in submember.findall('String'):
# ET.dump(submember) # Debug
entry = []
mkey = strmember.find('Key').text
try:
mval = strmember.find('Value').text
except Exception:
print('Info: No value given for key "%s" (group: %s), moving on ...' % (mkey, currGroupName))
mval = u''
mydict[mkey] = mval
entry = []
sep = u''
if currGroupName != u'':
sep = u'--'
entry.append(escape(currGroupName + sep + mydict['Title']))
entry.append(u'UserName')
if u'UserName' in mydict:
entry.append(escape(mydict['UserName']))
else:
entry.append(u'')
entry.append(u'')
csvwriter.writerow(entry)
entry_list.append(entry)
entry = []
entry.append(escape(currGroupName + sep + mydict['Title']))
entry.append(u'Password')
if u'Password' in mydict:
entry.append(escape(mydict['Password']))
else:
entry.append(u'')
entry.append(u'')
csvwriter.writerow(entry)
entry_list.append(entry)
entry = []
entry.append(escape(currGroupName + sep + mydict['Title']))
entry.append(u'URL')
if u'URL' in mydict:
entry.append(escape(mydict['URL']))
else:
entry.append(u'')
entry.append(u'')
csvwriter.writerow(entry)
entry_list.append(entry)
entry = []
entry.append(escape(currGroupName + sep + mydict['Title']))
entry.append(u'Notes')
entry.append(u'')
if u'Notes' in mydict:
entry.append(escape(mydict['Notes']))
else:
entry.append(u'')
entry_list.append(entry)
try:
# This works in Python 3
# This fails in Python 2.7 because ASCII codec cannot hold Unicode chars
csvwriter.writerow(entry)
except UnicodeEncodeError:
# This code works under Python 2.7
# The strings in the list can contain Unicode chars.
# Writerow() writes ASCII.
# Unicode chars do not fit into ASCII, so we have to encode strings first.
entryunicode = []
for el in entry:
entryunicode.append(el.encode('utf-8'))
csvwriter.writerow(entryunicode)
mydict.clear()
del mydict
def main():
parseArgs(sys.argv[1:])
print('Starting to parse input file "%s".' % (ifile))
tree = ET.parse(ifile)
root = tree.getroot()
# open a file for writing
with open(ofile, 'w') as f:
root = root.find('Root')
max = calculateDepth(root, 0)
print('There are %d levels in the hierarchy of the XML file.' % max)
csv.register_dialect('escaped', doublequote=False, escapechar='\\')
# create the csv writer object
csvwriter = csv.writer(f, dialect='escaped')
entry_list = []
# entry = []
# entry.append('Groupname')
# entry.append('Key')
# entry.append('Value/Password')
# entry.append('Comments')
# csvwriter.writerow(entry)
writeEntriesToList(root, 0, u'', max, entry_list, csvwriter)
print('Produced output file "%s".' % ofile)
print('Entries are in 4 columns as follows: Groupname, Key, Value/Password, Comments')
# printCsv(ofile) # Debug
if __name__ == '__main__':
main()