min=0 makes more sense than min=-1
[mediagoblin.git] / mediagoblin / auth / lib.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 Free Software Foundation, Inc
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 import os
18 import random
19
20 import bcrypt
21
22
23 def bcrypt_check_password(raw_pass, stored_hash, extra_salt=None):
24 """
25 Check to see if this password matches.
26
27 Args:
28 - raw_pass: user submitted password to check for authenticity.
29 - stored_hash: The hash of the raw password (and possibly extra
30 salt) to check against
31 - extra_salt: (optional) If this password is with stored with a
32 non-database extra salt (probably in the config file) for extra
33 security, factor this into the check.
34
35 Returns:
36 True or False depending on success.
37 """
38 if extra_salt:
39 raw_pass = u"%s:%s" % (extra_salt, raw_pass)
40
41 hashed_pass = bcrypt.hashpw(raw_pass, stored_hash)
42
43 # Reduce risk of timing attacks by hashing again with a random
44 # number (thx to zooko on this advice, which I hopefully
45 # incorporated right.)
46 #
47 # See also:
48 rand_salt = bcrypt.gensalt(5)
49 randplus_stored_hash = bcrypt.hashpw(stored_hash, rand_salt)
50 randplus_hashed_pass = bcrypt.hashpw(hashed_pass, rand_salt)
51
52 return randplus_stored_hash == randplus_hashed_pass
53
54
55 def bcrypt_gen_password_hash(raw_pass, extra_salt=None):
56 """
57 Generate a salt for this new password.
58
59 Args:
60 - raw_pass: user submitted password
61 - extra_salt: (optional) If this password is with stored with a
62 non-database extra salt
63 """
64 if extra_salt:
65 raw_pass = u"%s:%s" % (extra_salt, raw_pass)
66
67 return unicode(bcrypt.hashpw(raw_pass, bcrypt.gensalt()))
68
69
70 def fake_login_attempt():
71 """
72 Pretend we're trying to login.
73
74 Nothing actually happens here, we're just trying to take up some
75 time, approximately the same amount of time as
76 bcrypt_check_password, so as to avoid figuring out what users are
77 on the system by intentionally faking logins a bunch of times.
78 """
79 rand_salt = bcrypt.gensalt(5)
80
81 hashed_pass = bcrypt.hashpw(str(random.random()), rand_salt)
82
83 randplus_stored_hash = bcrypt.hashpw(str(random.random()), rand_salt)
84 randplus_hashed_pass = bcrypt.hashpw(hashed_pass, rand_salt)
85
86 randplus_stored_hash == randplus_hashed_pass