Utilities/crypto/py3PBKDF2.py

changeset 1127
b1802ebe0066
child 1130
3e9f0330f833
equal deleted inserted replaced
1123:4ed80ed7e433 1127:b1802ebe0066
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2002 - 2011 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 def pbkdf2(password, salt, iterations, digestMod):
27 """
28 Module function to hash a password according to the PBKDF2 specification.
29
30 @param password clear text password (bytes)
31 @param salt salt value (bytes)
32 @param iterations number of times hash function should be applied (integer)
33 @param digestMod hash function
34 @return hashed password (bytes)
35 """
36 hash = password
37 for i in range(iterations):
38 hash = hmac.new(salt, hash, digestMod).digest()
39 return hash
40
41
42 def hashPasswordTuple(password, digestMod=hashlib.sha512, iterations=10000, saltSize=32):
43 """
44 Module function to hash a password according to the PBKDF2 specification.
45
46 @param password clear text password (string)
47 @param digestMod hash function
48 @param iterations number of times hash function should be applied (integer)
49 @param saltSize size of the salt (integer)
50 @return tuple of digestname (string), number of iterations (integer),
51 salt (bytes) and hashed password (bytes)
52 """
53 salt = os.urandom(saltSize)
54 password = password.encode("utf-8")
55 hash = pbkdf2(password, salt, iterations, digestMod)
56 digestname = digestMod.__name__.replace("openssl_", "")
57 return digestname, iterations, salt, hash
58
59
60 def hashPassword(password, digestMod=hashlib.sha512, iterations=10000, saltSize=32):
61 """
62 Module function to hash a password according to the PBKDF2 specification.
63
64 @param password clear text password (string)
65 @param digestMod hash function
66 @param iterations number of times hash function should be applied (integer)
67 @param saltSize size of the salt (integer)
68 @return hashed password entry according to PBKDF2 specification (string)
69 """
70 digestname, iterations, salt, hash = \
71 hashPasswordTuple(password, digestMod, iterations, saltSize)
72 return Delimiter.join([
73 digestname,
74 str(iterations),
75 base64.b64encode(salt).decode("ascii"),
76 base64.b64encode(hash).decode("ascii")
77 ])
78
79
80 def verifyPassword(password, hash):
81 """
82 Module function to verify a password against a hash encoded password.
83
84 @param password clear text password (string)
85 @param hash hash encoded password in the form
86 'digestmod$iterations$salt$hashed_password' as produced by the
87 hashPassword function (string)
88 @return flag indicating a successfull verification (boolean)
89 @exception ValueError the hash is not of the expected format or the
90 digest is not one of the known ones
91 """
92 try:
93 digestname, iterations, salt, pwHash = hash.split(Delimiter)
94 except ValueError:
95 raise ValueError(
96 "Expected hash encoded password in format "\
97 "'digestmod{0}iterations{0}salt{0}hashed_password".format(Delimiter))
98
99 if digestname not in Hashes.keys():
100 raise ValueError(
101 "Unsupported hash algorithm '{0}' for hash encoded password '{1}'.".format(
102 digestname, hash))
103
104 iterations = int(iterations)
105 salt = base64.b64decode(salt.encode("ascii"))
106 pwHash = base64.b64decode(pwHash.encode("ascii"))
107 password = password.encode("utf-8")
108 return pwHash == pbkdf2(password, salt, iterations, Hashes[digestname])
109
110
111 def rehashPassword(password, hashParameters):
112 """
113 Module function to recreate a password hash given the hash parameters.
114
115 @param password clear text password (string)
116 @param hashParameters hash parameters in the form
117 'digestmod$iterations$salt' (string)
118 @return hashed password (bytes)
119 @exception ValueError the hash parameters string is not of the expected format
120 or the digest is not one of the known ones
121 """
122 try:
123 digestname, iterations, salt = hashParameters.split(Delimiter)
124 except ValueError:
125 raise ValueError(
126 "Expected hash parameters string in format "\
127 "'digestmod{0}iterations{0}salt".format(Delimiter))
128
129 if digestname not in Hashes.keys():
130 raise ValueError(
131 "Unsupported hash algorithm '{0}' for hash parameters '{1}'.".format(
132 digestname, hash))
133
134 iterations = int(iterations)
135 salt = base64.b64decode(salt.encode("ascii"))
136 password = password.encode("utf-8")
137 return pbkdf2(password, salt, iterations, Hashes[digestname])
138
139
140 if __name__ == "__main__":
141 import sys
142 pw = "secret_password"
143 print(len(hashPasswordTuple(pw)[-1]))
144 pwHash = hashPassword(pw)
145 print(pwHash)
146 print(verifyPassword(pw, pwHash))
147 sys.exit(0)

eric ide

mercurial