src/eric7/Utilities/crypto/__init__.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
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")))
172 180
173 181
174 def pwReencrypt(epw, oldPassword, newPassword): 182 def pwReencrypt(epw, oldPassword, newPassword):
175 """ 183 """
176 Module function to re-encrypt a password. 184 Module function to re-encrypt a password.
177 185
178 @param epw hashed password to re-encrypt (string) 186 @param epw hashed password to re-encrypt (string)
179 @param oldPassword password used to encrypt (string) 187 @param oldPassword password used to encrypt (string)
180 @param newPassword new password to be used (string) 188 @param newPassword new password to be used (string)
181 @return encrypted password (string) and flag indicating 189 @return encrypted password (string) and flag indicating
182 success (boolean) 190 success (boolean)
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]

eric ide

mercurial