eric6/Utilities/crypto/py3PBKDF2.py

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

eric ide

mercurial