31 |
31 |
32 |
32 |
33 def pwEncode(pw): |
33 def pwEncode(pw): |
34 """ |
34 """ |
35 Module function to encode a password. |
35 Module function to encode a password. |
36 |
36 |
37 @param pw password to encode (string) |
37 @param pw password to encode (string) |
38 @return encoded password (string) |
38 @return encoded password (string) |
39 """ |
39 """ |
40 pop = ( |
40 pop = ( |
41 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" |
41 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ".,;:-_!$?*+#" |
42 ".,;:-_!$?*+#" |
42 ) |
43 ) |
43 rpw = "".join(random.sample(pop, 32)) + pw + "".join(random.sample(pop, 32)) |
44 rpw = ( |
|
45 "".join(random.sample(pop, 32)) + |
|
46 pw + |
|
47 "".join(random.sample(pop, 32)) |
|
48 ) |
|
49 return EncodeMarker + base64.b64encode(rpw.encode("utf-8")).decode("ascii") |
44 return EncodeMarker + base64.b64encode(rpw.encode("utf-8")).decode("ascii") |
50 |
45 |
51 |
46 |
52 def pwDecode(epw): |
47 def pwDecode(epw): |
53 """ |
48 """ |
54 Module function to decode a password. |
49 Module function to decode a password. |
55 |
50 |
56 @param epw encoded password to decode (string) |
51 @param epw encoded password to decode (string) |
57 @return decoded password (string) |
52 @return decoded password (string) |
58 """ |
53 """ |
59 if not epw.startswith(EncodeMarker): |
54 if not epw.startswith(EncodeMarker): |
60 return epw # it was not encoded using pwEncode |
55 return epw # it was not encoded using pwEncode |
61 |
56 |
62 return base64.b64decode(epw[3:].encode("ascii"))[32:-32].decode("utf-8") |
57 return base64.b64decode(epw[3:].encode("ascii"))[32:-32].decode("utf-8") |
63 |
58 |
64 |
59 |
65 def __getMasterPassword(): |
60 def __getMasterPassword(): |
66 """ |
61 """ |
67 Private module function to get the password from the user. |
62 Private module function to get the password from the user. |
68 """ |
63 """ |
69 global MasterPassword |
64 global MasterPassword |
70 |
65 |
71 pw, ok = QInputDialog.getText( |
66 pw, ok = QInputDialog.getText( |
72 None, |
67 None, |
73 QCoreApplication.translate("Crypto", "Master Password"), |
68 QCoreApplication.translate("Crypto", "Master Password"), |
74 QCoreApplication.translate("Crypto", "Enter the master password:"), |
69 QCoreApplication.translate("Crypto", "Enter the master password:"), |
75 QLineEdit.EchoMode.Password) |
70 QLineEdit.EchoMode.Password, |
|
71 ) |
76 if ok: |
72 if ok: |
77 from .py3PBKDF2 import verifyPassword |
73 from .py3PBKDF2 import verifyPassword |
|
74 |
78 masterPassword = Preferences.getUser("MasterPassword") |
75 masterPassword = Preferences.getUser("MasterPassword") |
79 try: |
76 try: |
80 if masterPassword: |
77 if masterPassword: |
81 if verifyPassword(pw, masterPassword): |
78 if verifyPassword(pw, masterPassword): |
82 MasterPassword = pwEncode(pw) |
79 MasterPassword = pwEncode(pw) |
83 else: |
80 else: |
84 EricMessageBox.warning( |
81 EricMessageBox.warning( |
85 None, |
82 None, |
|
83 QCoreApplication.translate("Crypto", "Master Password"), |
86 QCoreApplication.translate( |
84 QCoreApplication.translate( |
87 "Crypto", "Master Password"), |
85 "Crypto", """The given password is incorrect.""" |
88 QCoreApplication.translate( |
86 ), |
89 "Crypto", |
87 ) |
90 """The given password is incorrect.""")) |
|
91 else: |
88 else: |
92 EricMessageBox.critical( |
89 EricMessageBox.critical( |
93 None, |
90 None, |
94 QCoreApplication.translate("Crypto", "Master Password"), |
91 QCoreApplication.translate("Crypto", "Master Password"), |
95 QCoreApplication.translate( |
92 QCoreApplication.translate( |
96 "Crypto", |
93 "Crypto", """There is no master password registered.""" |
97 """There is no master password registered.""")) |
94 ), |
|
95 ) |
98 except ValueError as why: |
96 except ValueError as why: |
99 EricMessageBox.warning( |
97 EricMessageBox.warning( |
100 None, |
98 None, |
101 QCoreApplication.translate("Crypto", "Master Password"), |
99 QCoreApplication.translate("Crypto", "Master Password"), |
102 QCoreApplication.translate( |
100 QCoreApplication.translate( |
103 "Crypto", |
101 "Crypto", |
104 """<p>The given password cannot be verified.</p>""" |
102 """<p>The given password cannot be verified.</p>""" |
105 """<p>Reason: {0}""".format(str(why)))) |
103 """<p>Reason: {0}""".format(str(why)), |
|
104 ), |
|
105 ) |
106 |
106 |
107 |
107 |
108 def pwEncrypt(pw, masterPW=None): |
108 def pwEncrypt(pw, masterPW=None): |
109 """ |
109 """ |
110 Module function to encrypt a password. |
110 Module function to encrypt a password. |
111 |
111 |
112 @param pw password to encrypt (string) |
112 @param pw password to encrypt (string) |
113 @param masterPW password to be used for encryption (string) |
113 @param masterPW password to be used for encryption (string) |
114 @return encrypted password (string) and flag indicating |
114 @return encrypted password (string) and flag indicating |
115 success (boolean) |
115 success (boolean) |
116 """ |
116 """ |
117 if masterPW is None: |
117 if masterPW is None: |
118 if MasterPassword is None: |
118 if MasterPassword is None: |
119 __getMasterPassword() |
119 __getMasterPassword() |
120 if MasterPassword is None: |
120 if MasterPassword is None: |
121 return "", False |
121 return "", False |
122 |
122 |
123 masterPW = pwDecode(MasterPassword) |
123 masterPW = pwDecode(MasterPassword) |
124 |
124 |
125 from .py3PBKDF2 import hashPasswordTuple |
125 from .py3PBKDF2 import hashPasswordTuple |
|
126 |
126 digestname, iterations, salt, pwHash = hashPasswordTuple(masterPW) |
127 digestname, iterations, salt, pwHash = hashPasswordTuple(masterPW) |
127 key = pwHash[:32] |
128 key = pwHash[:32] |
128 from .py3AES import encryptData |
129 from .py3AES import encryptData |
|
130 |
129 try: |
131 try: |
130 cipher = encryptData(key, pw.encode("utf-8")) |
132 cipher = encryptData(key, pw.encode("utf-8")) |
131 except ValueError: |
133 except ValueError: |
132 return "", False |
134 return "", False |
133 return CryptoMarker + Delimiter.join([ |
135 return ( |
134 digestname, |
136 CryptoMarker |
135 str(iterations), |
137 + Delimiter.join( |
136 base64.b64encode(salt).decode("ascii"), |
138 [ |
137 base64.b64encode(cipher).decode("ascii") |
139 digestname, |
138 ]), True |
140 str(iterations), |
|
141 base64.b64encode(salt).decode("ascii"), |
|
142 base64.b64encode(cipher).decode("ascii"), |
|
143 ] |
|
144 ), |
|
145 True, |
|
146 ) |
139 |
147 |
140 |
148 |
141 def pwDecrypt(epw, masterPW=None): |
149 def pwDecrypt(epw, masterPW=None): |
142 """ |
150 """ |
143 Module function to decrypt a password. |
151 Module function to decrypt a password. |
144 |
152 |
145 @param epw hashed password to decrypt (string) |
153 @param epw hashed password to decrypt (string) |
146 @param masterPW password to be used for decryption (string) |
154 @param masterPW password to be used for decryption (string) |
147 @return decrypted password (string) and flag indicating |
155 @return decrypted password (string) and flag indicating |
148 success (boolean) |
156 success (boolean) |
149 """ |
157 """ |
150 if not epw.startswith(CryptoMarker): |
158 if not epw.startswith(CryptoMarker): |
151 return epw, False # it was not encoded using pwEncrypt |
159 return epw, False # it was not encoded using pwEncrypt |
152 |
160 |
153 if masterPW is None: |
161 if masterPW is None: |
154 if MasterPassword is None: |
162 if MasterPassword is None: |
155 __getMasterPassword() |
163 __getMasterPassword() |
156 if MasterPassword is None: |
164 if MasterPassword is None: |
157 return "", False |
165 return "", False |
158 |
166 |
159 masterPW = pwDecode(MasterPassword) |
167 masterPW = pwDecode(MasterPassword) |
160 |
168 |
161 from .py3AES import decryptData |
169 from .py3AES import decryptData |
162 from .py3PBKDF2 import rehashPassword |
170 from .py3PBKDF2 import rehashPassword |
163 |
171 |
164 hashParameters, epw = epw[3:].rsplit(Delimiter, 1) |
172 hashParameters, epw = epw[3:].rsplit(Delimiter, 1) |
165 try: |
173 try: |
166 # recreate the key used to encrypt |
174 # recreate the key used to encrypt |
167 key = rehashPassword(masterPW, hashParameters)[:32] |
175 key = rehashPassword(masterPW, hashParameters)[:32] |
168 plaintext = decryptData(key, base64.b64decode(epw.encode("ascii"))) |
176 plaintext = decryptData(key, base64.b64decode(epw.encode("ascii"))) |
189 |
197 |
190 |
198 |
191 def pwRecode(epw, oldPassword, newPassword): |
199 def pwRecode(epw, oldPassword, newPassword): |
192 """ |
200 """ |
193 Module function to re-encode a password. |
201 Module function to re-encode a password. |
194 |
202 |
195 In case of an error the encoded password is returned unchanged. |
203 In case of an error the encoded password is returned unchanged. |
196 |
204 |
197 @param epw encoded password to re-encode (string) |
205 @param epw encoded password to re-encode (string) |
198 @param oldPassword password used to encode (string) |
206 @param oldPassword password used to encode (string) |
199 @param newPassword new password to be used (string) |
207 @param newPassword new password to be used (string) |
200 @return encoded password (string) |
208 @return encoded password (string) |
201 """ |
209 """ |
202 if epw == "": |
210 if epw == "": |
203 return epw |
211 return epw |
204 |
212 |
205 if newPassword == "": |
213 if newPassword == "": |
206 plaintext, ok = pwDecrypt(epw) |
214 plaintext, ok = pwDecrypt(epw) |
207 return (pwEncode(plaintext) if ok else epw) |
215 return pwEncode(plaintext) if ok else epw |
208 else: |
216 else: |
209 if oldPassword == "": |
217 if oldPassword == "": |
210 plaintext = pwDecode(epw) |
218 plaintext = pwDecode(epw) |
211 cipher, ok = pwEncrypt(plaintext, newPassword) |
219 cipher, ok = pwEncrypt(plaintext, newPassword) |
212 return (cipher if ok else epw) |
220 return cipher if ok else epw |
213 else: |
221 else: |
214 npw, ok = pwReencrypt(epw, oldPassword, newPassword) |
222 npw, ok = pwReencrypt(epw, oldPassword, newPassword) |
215 return (npw if ok else epw) |
223 return npw if ok else epw |
216 |
224 |
217 |
225 |
218 def pwConvert(pw, encode=True): |
226 def pwConvert(pw, encode=True): |
219 """ |
227 """ |
220 Module function to convert a plaintext password to the encoded form or |
228 Module function to convert a plaintext password to the encoded form or |
221 vice versa. |
229 vice versa. |
222 |
230 |
223 If there is an error, an empty code is returned for the encode function |
231 If there is an error, an empty code is returned for the encode function |
224 or the given encoded password for the decode function. |
232 or the given encoded password for the decode function. |
225 |
233 |
226 @param pw password to encode (string) |
234 @param pw password to encode (string) |
227 @param encode flag indicating an encode or decode function (boolean) |
235 @param encode flag indicating an encode or decode function (boolean) |
228 @return encoded or decoded password (string) |
236 @return encoded or decoded password (string) |
229 """ |
237 """ |
230 if pw == "": |
238 if pw == "": |
231 return pw |
239 return pw |
232 |
240 |
233 if encode: |
241 if encode: |
234 # plain text -> encoded |
242 # plain text -> encoded |
235 if Preferences.getUser("UseMasterPassword"): |
243 if Preferences.getUser("UseMasterPassword"): |
236 epw = pwEncrypt(pw)[0] |
244 epw = pwEncrypt(pw)[0] |
237 else: |
245 else: |
241 # encoded -> plain text |
249 # encoded -> plain text |
242 if Preferences.getUser("UseMasterPassword"): |
250 if Preferences.getUser("UseMasterPassword"): |
243 plain, ok = pwDecrypt(pw) |
251 plain, ok = pwDecrypt(pw) |
244 else: |
252 else: |
245 plain, ok = pwDecode(pw), True |
253 plain, ok = pwDecode(pw), True |
246 return (plain if ok else pw) |
254 return plain if ok else pw |
247 |
255 |
248 |
256 |
249 def changeRememberedMaster(newPassword): |
257 def changeRememberedMaster(newPassword): |
250 """ |
258 """ |
251 Module function to change the remembered master password. |
259 Module function to change the remembered master password. |
252 |
260 |
253 @param newPassword new password to be used (string) |
261 @param newPassword new password to be used (string) |
254 """ |
262 """ |
255 global MasterPassword |
263 global MasterPassword |
256 MasterPassword = pwEncode(newPassword) if newPassword else None |
264 MasterPassword = pwEncode(newPassword) if newPassword else None |
257 |
265 |
258 |
266 |
259 def dataEncrypt(data, password, keyLength=32, hashIterations=10000): |
267 def dataEncrypt(data, password, keyLength=32, hashIterations=10000): |
260 """ |
268 """ |
261 Module function to encrypt a password. |
269 Module function to encrypt a password. |
262 |
270 |
263 @param data data to encrypt (bytes) |
271 @param data data to encrypt (bytes) |
264 @param password password to be used for encryption (string) |
272 @param password password to be used for encryption (string) |
265 @param keyLength length of the key to be generated for encryption |
273 @param keyLength length of the key to be generated for encryption |
266 (16, 24 or 32) |
274 (16, 24 or 32) |
267 @param hashIterations number of hashes to be applied to the password for |
275 @param hashIterations number of hashes to be applied to the password for |
269 @return encrypted data (bytes) and flag indicating |
277 @return encrypted data (bytes) and flag indicating |
270 success (boolean) |
278 success (boolean) |
271 """ |
279 """ |
272 from .py3AES import encryptData |
280 from .py3AES import encryptData |
273 from .py3PBKDF2 import hashPasswordTuple |
281 from .py3PBKDF2 import hashPasswordTuple |
274 |
282 |
275 digestname, iterations, salt, pwHash = hashPasswordTuple( |
283 digestname, iterations, salt, pwHash = hashPasswordTuple( |
276 password, iterations=hashIterations) |
284 password, iterations=hashIterations |
|
285 ) |
277 key = pwHash[:keyLength] |
286 key = pwHash[:keyLength] |
278 try: |
287 try: |
279 cipher = encryptData(key, data) |
288 cipher = encryptData(key, data) |
280 except ValueError: |
289 except ValueError: |
281 return b"", False |
290 return b"", False |
282 return CryptoMarker.encode("utf-8") + Delimiter.encode("utf-8").join([ |
291 return ( |
283 digestname.encode("utf-8"), |
292 CryptoMarker.encode("utf-8") |
284 str(iterations).encode("utf-8"), |
293 + Delimiter.encode("utf-8").join( |
285 base64.b64encode(salt), |
294 [ |
286 base64.b64encode(cipher) |
295 digestname.encode("utf-8"), |
287 ]), True |
296 str(iterations).encode("utf-8"), |
|
297 base64.b64encode(salt), |
|
298 base64.b64encode(cipher), |
|
299 ] |
|
300 ), |
|
301 True, |
|
302 ) |
288 |
303 |
289 |
304 |
290 def dataDecrypt(edata, password, keyLength=32): |
305 def dataDecrypt(edata, password, keyLength=32): |
291 """ |
306 """ |
292 Module function to decrypt a password. |
307 Module function to decrypt a password. |
293 |
308 |
294 @param edata hashed data to decrypt (string) |
309 @param edata hashed data to decrypt (string) |
295 @param password password to be used for decryption (string) |
310 @param password password to be used for decryption (string) |
296 @param keyLength length of the key to be generated for decryption |
311 @param keyLength length of the key to be generated for decryption |
297 (16, 24 or 32) |
312 (16, 24 or 32) |
298 @return decrypted data (bytes) and flag indicating |
313 @return decrypted data (bytes) and flag indicating |
299 success (boolean) |
314 success (boolean) |
300 """ |
315 """ |
301 if not edata.startswith(CryptoMarker.encode("utf-8")): |
316 if not edata.startswith(CryptoMarker.encode("utf-8")): |
302 return edata, False # it was not encoded using dataEncrypt |
317 return edata, False # it was not encoded using dataEncrypt |
303 |
318 |
304 from .py3AES import decryptData |
319 from .py3AES import decryptData |
305 from .py3PBKDF2 import rehashPassword |
320 from .py3PBKDF2 import rehashPassword |
306 |
321 |
307 hashParametersBytes, edata = edata[3:].rsplit(Delimiter.encode("utf-8"), 1) |
322 hashParametersBytes, edata = edata[3:].rsplit(Delimiter.encode("utf-8"), 1) |
308 hashParameters = hashParametersBytes.decode() |
323 hashParameters = hashParametersBytes.decode() |
309 try: |
324 try: |
310 # recreate the key used to encrypt |
325 # recreate the key used to encrypt |
311 key = rehashPassword(password, hashParameters)[:keyLength] |
326 key = rehashPassword(password, hashParameters)[:keyLength] |