src/eric7/Utilities/crypto/py3PBKDF2.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing PBKDF2 functions.
8 """
9
10 import hashlib
11 import hmac
12 import os
13 import base64
14
15 Hashes = {
16 "sha1": hashlib.sha1,
17 "sha224": hashlib.sha224,
18 "sha256": hashlib.sha256,
19 "sha384": hashlib.sha384,
20 "sha512": hashlib.sha512,
21 "md5": hashlib.md5,
22 }
23
24 Delimiter = "$"
25
26
27 def pbkdf2(password, salt, iterations, digestMod):
28 """
29 Module function to hash a password according to the PBKDF2 specification.
30
31 @param password clear text password (bytes)
32 @param salt salt value (bytes)
33 @param iterations number of times hash function should be applied (integer)
34 @param digestMod hash function
35 @return hashed password (bytes)
36 """
37 pwHash = password
38 for _ in range(iterations):
39 pwHash = hmac.new(salt, pwHash, digestMod).digest()
40 return pwHash
41
42
43 def hashPasswordTuple(password, digestMod=hashlib.sha512, iterations=10000,
44 saltSize=32):
45 """
46 Module function to hash a password according to the PBKDF2 specification.
47
48 @param password clear text password (string)
49 @param digestMod hash function
50 @param iterations number of times hash function should be applied (integer)
51 @param saltSize size of the salt (integer)
52 @return tuple of digestname (string), number of iterations (integer),
53 salt (bytes) and hashed password (bytes)
54 """
55 salt = os.urandom(saltSize)
56 password = password.encode("utf-8")
57 pwHash = pbkdf2(password, salt, iterations, digestMod)
58 digestname = digestMod.__name__.replace("openssl_", "")
59 return digestname, iterations, salt, pwHash
60
61
62 def hashPassword(password, digestMod=hashlib.sha512, iterations=10000,
63 saltSize=32):
64 """
65 Module function to hash a password according to the PBKDF2 specification.
66
67 @param password clear text password (string)
68 @param digestMod hash function
69 @param iterations number of times hash function should be applied (integer)
70 @param saltSize size of the salt (integer)
71 @return hashed password entry according to PBKDF2 specification (string)
72 """
73 digestname, iterations, salt, pwHash = hashPasswordTuple(
74 password, digestMod, iterations, saltSize)
75 return Delimiter.join([
76 digestname,
77 str(iterations),
78 base64.b64encode(salt).decode("ascii"),
79 base64.b64encode(pwHash).decode("ascii")
80 ])
81
82
83 def verifyPassword(password, pwHash):
84 """
85 Module function to verify a password against a hash encoded password.
86
87 @param password clear text password (string)
88 @param pwHash hash encoded password in the form
89 'digestmod$iterations$salt$hashed_password' as produced by the
90 hashPassword function (string)
91 @return flag indicating a successfull verification (boolean)
92 @exception ValueError the hash is not of the expected format or the
93 digest is not one of the known ones
94 """
95 try:
96 digestname, iterations, salt, pwHash = pwHash.split(Delimiter)
97 except ValueError:
98 raise ValueError(
99 "Expected hash encoded password in format "
100 "'digestmod{0}iterations{0}salt{0}hashed_password"
101 .format(Delimiter))
102
103 if digestname not in Hashes.keys():
104 raise ValueError(
105 "Unsupported hash algorithm '{0}' for hash encoded password '{1}'."
106 .format(digestname, pwHash))
107
108 iterations = int(iterations)
109 salt = base64.b64decode(salt.encode("ascii"))
110 pwHash = base64.b64decode(pwHash.encode("ascii"))
111 password = password.encode("utf-8")
112 return pwHash == pbkdf2(password, salt, iterations, Hashes[digestname])
113
114
115 def rehashPassword(password, hashParameters):
116 """
117 Module function to recreate a password hash given the hash parameters.
118
119 @param password clear text password (string)
120 @param hashParameters hash parameters in the form
121 'digestmod$iterations$salt' (string)
122 @return hashed password (bytes)
123 @exception ValueError the hash parameters string is not of the expected
124 format or the digest is not one of the known ones
125 """
126 try:
127 digestname, iterations, salt = hashParameters.split(Delimiter)
128 except ValueError:
129 raise ValueError(
130 "Expected hash parameters string in format "
131 "'digestmod{0}iterations{0}salt".format(Delimiter))
132
133 if digestname not in Hashes.keys():
134 raise ValueError(
135 "Unsupported hash algorithm '{0}' for hash parameters '{1}'."
136 .format(digestname, hashParameters))
137
138 iterations = int(iterations)
139 salt = base64.b64decode(salt.encode("ascii"))
140 password = password.encode("utf-8")
141 return pbkdf2(password, salt, iterations, Hashes[digestname])

eric ide

mercurial