9
9
from copy import deepcopy
10
10
import base64
11
11
from binascii import Error as BinasciiError
12
- import unicodedata
13
12
import zlib
14
13
import re
15
14
import codecs
@@ -105,7 +104,49 @@ def aes_kdf(key, rounds, key_composite):
105
104
return hashlib .sha256 (transformed_key ).digest ()
106
105
107
106
108
- def compute_key_composite (password = None , keyfile = None ):
107
+ def compute_keyfile_part_of_composite (keyfile ):
108
+ if hasattr (keyfile , "read" ):
109
+ keyfile_bytes = keyfile .read ()
110
+ else :
111
+ with open (keyfile , 'rb' ) as f :
112
+ keyfile_bytes = f .read ()
113
+ # try to read XML keyfile
114
+ try :
115
+ tree = etree .fromstring (keyfile_bytes )
116
+ version = tree .find ('Meta/Version' ).text
117
+ data_element = tree .find ('Key/Data' )
118
+ if version .startswith ('1.0' ):
119
+ return base64 .b64decode (data_element .text )
120
+ elif version .startswith ('2.0' ):
121
+ # read keyfile data and convert to bytes
122
+ keyfile_composite = bytes .fromhex (data_element .text .strip ())
123
+ # validate bytes against hash
124
+ hash = bytes .fromhex (data_element .attrib ['Hash' ])
125
+ hash_computed = hashlib .sha256 (keyfile_composite ).digest ()[:4 ]
126
+ assert hash == hash_computed , "Keyfile has invalid hash"
127
+ return keyfile_composite
128
+ # otherwise, try to read plain keyfile
129
+ except (etree .XMLSyntaxError , UnicodeDecodeError ):
130
+ try :
131
+ try :
132
+ int (keyfile_bytes , 16 )
133
+ is_hex = True
134
+ except ValueError :
135
+ is_hex = False
136
+ # if the length is 32 bytes we assume it is the key
137
+ if len (keyfile_bytes ) == 32 :
138
+ return keyfile_bytes
139
+ # if the length is 64 bytes we assume the key is hex encoded
140
+ elif len (keyfile_bytes ) == 64 and is_hex :
141
+ return codecs .decode (keyfile_bytes , 'hex' )
142
+ # anything else may be a file to hash for the key
143
+ else :
144
+ return hashlib .sha256 (keyfile_bytes ).digest ()
145
+ except :
146
+ raise IOError ('Could not read keyfile' )
147
+
148
+
149
+ def compute_key_composite (password = None , keyfile = None , additional_parts = None ):
109
150
"""Compute composite key.
110
151
Used in header verification and payload decryption."""
111
152
@@ -116,50 +157,17 @@ def compute_key_composite(password=None, keyfile=None):
116
157
password_composite = b''
117
158
# hash the keyfile
118
159
if keyfile :
119
- if hasattr (keyfile , "read" ):
120
- keyfile_bytes = keyfile .read ()
121
- else :
122
- with open (keyfile , 'rb' ) as f :
123
- keyfile_bytes = f .read ()
124
- # try to read XML keyfile
125
- try :
126
- tree = etree .fromstring (keyfile_bytes )
127
- version = tree .find ('Meta/Version' ).text
128
- data_element = tree .find ('Key/Data' )
129
- if version .startswith ('1.0' ):
130
- keyfile_composite = base64 .b64decode (data_element .text )
131
- elif version .startswith ('2.0' ):
132
- # read keyfile data and convert to bytes
133
- keyfile_composite = bytes .fromhex (data_element .text .strip ())
134
- # validate bytes against hash
135
- hash = bytes .fromhex (data_element .attrib ['Hash' ])
136
- hash_computed = hashlib .sha256 (keyfile_composite ).digest ()[:4 ]
137
- assert hash == hash_computed , "Keyfile has invalid hash"
138
- # otherwise, try to read plain keyfile
139
- except (etree .XMLSyntaxError , UnicodeDecodeError ):
140
- try :
141
- try :
142
- int (keyfile_bytes , 16 )
143
- is_hex = True
144
- except ValueError :
145
- is_hex = False
146
- # if the length is 32 bytes we assume it is the key
147
- if len (keyfile_bytes ) == 32 :
148
- keyfile_composite = keyfile_bytes
149
- # if the length is 64 bytes we assume the key is hex encoded
150
- elif len (keyfile_bytes ) == 64 and is_hex :
151
- keyfile_composite = codecs .decode (keyfile_bytes , 'hex' )
152
- # anything else may be a file to hash for the key
153
- else :
154
- keyfile_composite = hashlib .sha256 (keyfile_bytes ).digest ()
155
- except :
156
- raise IOError ('Could not read keyfile' )
157
-
160
+ keyfile_composite = compute_keyfile_part_of_composite (keyfile )
158
161
else :
159
162
keyfile_composite = b''
160
163
161
- # create composite key from password and keyfile composites
162
- return hashlib .sha256 (password_composite + keyfile_composite ).digest ()
164
+ # create composite key from password, keyfile, and other composites
165
+ overall_composite = password_composite + keyfile_composite
166
+ if additional_parts is not None :
167
+ for part in additional_parts :
168
+ overall_composite += part
169
+
170
+ return hashlib .sha256 (overall_composite ).digest ()
163
171
164
172
165
173
def compute_master (context ):
@@ -173,6 +181,30 @@ def compute_master(context):
173
181
return master_key
174
182
175
183
184
+ def populate_custom_data (kdbx , d ):
185
+ if len (d .keys ()) > 0 :
186
+ vd = Container (
187
+ version = b'\x00 \x01 ' ,
188
+ dict = d ,
189
+ )
190
+ kdbx .header .value .dynamic_header .update (
191
+ {
192
+ "public_custom_data" :
193
+ Container (
194
+ id = 'public_custom_data' ,
195
+ data = vd ,
196
+ next_byte = 0xFF ,
197
+ )
198
+ }
199
+ )
200
+ else :
201
+ # Removing header entirely
202
+ if "public_custom_data" in kdbx .header .value .dynamic_header :
203
+ del kdbx .header .value .dynamic_header ["public_custom_data" ]
204
+
205
+ kdbx .header .value .dynamic_header .move_to_end ("end" )
206
+
207
+
176
208
# -------------------- XML Processing --------------------
177
209
178
210
0 commit comments