forked from pinae/ctSESAM-pyside
-
Notifications
You must be signed in to change notification settings - Fork 0
/
kgk_manager.py
246 lines (210 loc) · 6.95 KB
/
kgk_manager.py
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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
The KGK manager stores the kgk and manages storage and encryption of kgk blocks.
"""
from preference_manager import PreferenceManager
from crypter import Crypter
from binascii import hexlify
import os
class KgkManager(object):
"""
New KgkManagers are uninitialized and need either a new kgk or get one by decrypting an existing one.
"""
def __init__(self):
self.preference_manager = None
self.kgk = b''
self.iv2 = None
self.salt2 = None
self.kgk_crypter = None
self.salt = b''
def __str__(self):
attr = ["KGK: " + str(hexlify(self.kgk), encoding='utf-8'),
"salt: " + str(hexlify(self.salt), encoding='utf-8')]
if self.iv2:
attr.append("iv2: " + str(hexlify(self.iv2), encoding='utf-8'))
if self.salt2:
attr.append("salt2: " + str(hexlify(self.salt2), encoding='utf-8'))
return "<" + ", ".join(attr) + ">"
def set_preference_manager(self, preference_manager):
"""
Pass a preference manager to load and store settings locally
:param preference_manager:
:type preference_manager: PreferenceManager
"""
if type(preference_manager) != PreferenceManager:
raise TypeError
self.preference_manager = preference_manager
def get_kgk_crypter_salt(self):
"""
Loads the public salt. If there is none it is created and stored.
:return:
"""
self.salt = self.preference_manager.get_salt()
if len(self.salt) != 32:
self.salt = Crypter.createSalt()
self.store_salt(self.salt)
return self.salt
def store_salt(self, salt):
"""
Stores the salt using the preference manager.
:param salt: the salt
:type salt: bytes
"""
if type(salt) == bytes:
self.salt = salt
if self.preference_manager:
self.preference_manager.store_salt(salt)
else:
raise TypeError("There is no salt to be saved")
def get_kgk_crypter(self, password, salt):
"""
Creates a kgk crypter for the given credentials. This is a very expensive operation.
:param password:
:type password: bytes
:param salt:
:type salt: bytes
:return: a kgk crypter
:rtype: Crypter
"""
self.kgk_crypter = Crypter(Crypter.createIvKey(password=password, salt=salt))
self.store_salt(salt=salt)
return self.kgk_crypter
def create_new_kgk(self):
"""
Creates a new kgk. This overwrites the previous one.
:return: the new kgk
:rtype: bytes
"""
self.kgk = os.urandom(64)
self.iv2 = Crypter.createIv()
self.salt2 = Crypter.createSalt()
return self.kgk
def decrypt_kgk(self, encrypted_kgk, kgk_crypter=None, password=b'', salt=b''):
"""
Decrypts kgk blobs. If a crypter is passed it is used. If none is passed a new crypter is created with
the salt and password. This takes relatively long. If the encrypted_kgk has a wrong length a new kgk is
created.
:param encrypted_kgk:
:type encrypted_kgk: bytes
:param kgk_crypter:
:type kgk_crypter: Crypter
:param password:
:type password: bytes
:param salt:
:type salt: bytes
"""
if kgk_crypter:
self.kgk_crypter = kgk_crypter
else:
if len(salt) < 32:
salt = Crypter.createSalt()
self.get_kgk_crypter(password, salt)
if len(encrypted_kgk) == 112:
kgk_block = self.kgk_crypter.decrypt_unpadded(encrypted_kgk)
self.salt2 = kgk_block[:32]
self.iv2 = kgk_block[32:48]
self.kgk = kgk_block[48:112]
else:
self.create_new_kgk()
def get_kgk(self):
"""
Returns the kgk.
:return: the kgk
:rtype: bytes
"""
return self.kgk
def has_kgk(self):
"""
Returns true if there is a kgk and a crypter.
:return: kgk state
:rtype: bool
"""
return not not self.kgk and len(self.kgk) == 64 and self.kgk_crypter
def get_salt2(self):
"""
Returns the salt2
:return: salt2
:rtype: bytes
"""
return self.salt2
def get_iv2(self):
"""
Returns the iv2
:return: iv2
:rtype: bytes
"""
return self.iv2
def fresh_salt2(self):
"""
Creates a fresh salt for the settings encryption (salt2).
"""
self.salt2 = Crypter.createSalt()
def fresh_iv2(self):
"""
Creates a fresh iv for the settings encryption (iv2).
"""
self.iv2 = Crypter.createIv()
def get_encrypted_kgk(self):
"""
Returns an encrypted kgk block.
:return: kgk block
:rtype: bytes
"""
return self.kgk_crypter.encrypt_unpadded(self.salt2 + self.iv2 + self.kgk)
def get_fresh_encrypted_kgk(self):
"""
Returns a new encrypted kgk block with fresh salt2 and iv2. This does not create a new kgk.
:return: kgk block
:rtype: bytes
"""
self.fresh_iv2()
self.fresh_salt2()
return self.get_encrypted_kgk()
def create_and_save_new_kgk_block(self, kgk_crypter=None):
"""
Creates a fresh kgk block and saves it.
:param kgk_crypter:
:type kgk_crypter: Crypter
:return: kgk block
:rtype: bytes
"""
self.salt = Crypter.createSalt()
self.store_salt(self.salt)
if kgk_crypter:
self.kgk_crypter = kgk_crypter
kgk_block = self.get_fresh_encrypted_kgk()
self.preference_manager.store_kgk_block(kgk_block)
return kgk_block
def update_from_blob(self, password, blob):
"""
Updates the kgk from a remote data blob.
:param password: the masterpassword
:type password: bytes
:param blob: the encrypted data
:type blob: bytes
"""
if blob[0] != 1 or len(blob) < 145:
raise ValueError("Version error: Wrong data format. Could not import anything.")
salt = blob[1:33]
kgk_block = blob[33:145]
self.decrypt_kgk(encrypted_kgk=kgk_block, password=password, salt=salt)
def store_local_kgk_block(self):
"""
Stores the local kgk block.
"""
if self.preference_manager:
self.preference_manager.store_kgk_block(self.get_encrypted_kgk())
if len(self.salt) == 32:
self.store_salt(self.salt)
else:
raise ValueError("The salt has to be 32 bytes.")
def reset(self):
"""
Resets the kgk manager.
"""
self.salt = b''
self.iv2 = None
self.salt2 = None
self.kgk = b''
self.kgk_crypter = None