diff options
-rw-r--r-- | mediagoblin/auth/__init__.py | 0 | ||||
-rw-r--r-- | mediagoblin/auth/lib.py | 66 | ||||
-rw-r--r-- | mediagoblin/tests/__init__.py | 0 | ||||
-rw-r--r-- | mediagoblin/tests/test_auth.py | 49 | ||||
-rw-r--r-- | setup.py | 3 |
5 files changed, 118 insertions, 0 deletions
diff --git a/mediagoblin/auth/__init__.py b/mediagoblin/auth/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/mediagoblin/auth/__init__.py diff --git a/mediagoblin/auth/lib.py b/mediagoblin/auth/lib.py new file mode 100644 index 00000000..29b955a0 --- /dev/null +++ b/mediagoblin/auth/lib.py @@ -0,0 +1,66 @@ +# GNU Mediagoblin -- federated, autonomous media hosting +# Copyright (C) 2011 Free Software Foundation, Inc +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import os + +import bcrypt + + +def bcrypt_check_password(raw_pass, stored_hash, extra_salt=None): + """ + Check to see if this password matches. + + Args: + - raw_pass: user submitted password to check for authenticity. + - stored_hash: The hash of the raw password (and possibly extra + salt) to check against + - extra_salt: (optional) If this password is with stored with a + non-database extra salt (probably in the config file) for extra + security, factor this into the check. + + Returns: + True or False depending on success. + """ + if extra_salt: + raw_pass = u"%s:%s" % (extra_salt, raw_pass) + + hashed_pass = bcrypt.hashpw(raw_pass, stored_hash) + + # Reduce risk of timing attacks by hashing again with a random + # number (thx to zooko on this advice, which I hopefully + # incorporated right.) + # + # See also: + rand_salt = bcrypt.gensalt(5) + randplus_stored_hash = bcrypt.hashpw(stored_hash, rand_salt) + randplus_hashed_pass = bcrypt.hashpw(hashed_pass, rand_salt) + + return randplus_stored_hash == randplus_hashed_pass + + +def bcrypt_gen_password_hash(raw_pass, extra_salt=None): + """ + Generate a salt for this new password. + + Args: + - raw_pass: user submitted password + - extra_salt: (optional) If this password is with stored with a + non-database extra salt + """ + if extra_salt: + raw_pass = u"%s:%s" % (extra_salt, raw_pass) + + return bcrypt.hashpw(raw_pass, bcrypt.gensalt()) diff --git a/mediagoblin/tests/__init__.py b/mediagoblin/tests/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/mediagoblin/tests/__init__.py diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py new file mode 100644 index 00000000..5b66bb3c --- /dev/null +++ b/mediagoblin/tests/test_auth.py @@ -0,0 +1,49 @@ +# GNU Mediagoblin -- federated, autonomous media hosting +# Copyright (C) 2011 Free Software Foundation, Inc +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from mediagoblin.auth import lib as auth_lib + + +######################## +# Test bcrypt auth funcs +######################## + +def test_bcrypt_check_password(): + # Check known 'lollerskates' password against check function + assert auth_lib.bcrypt_check_password( + 'lollerskates', + '$2a$12$PXU03zfrVCujBhVeICTwtOaHTUs5FFwsscvSSTJkqx/2RQ0Lhy/nO') + + # Same thing, but with extra fake salt. + assert auth_lib.bcrypt_check_password( + 'lollerskates', + '$2a$12$ELVlnw3z1FMu6CEGs/L8XO8vl0BuWSlUHgh0rUrry9DUXGMUNWwl6', + '3><7R45417') + + +def test_bcrypt_gen_password_hash(): + pw = 'youwillneverguessthis' + + # Normal password hash generation, and check on that hash + hashed_pw = auth_lib.bcrypt_gen_password_hash(pw) + assert auth_lib.bcrypt_check_password( + pw, hashed_pw) + + # Same thing, extra salt. + hashed_pw = auth_lib.bcrypt_gen_password_hash(pw, '3><7R45417') + assert auth_lib.bcrypt_check_password( + pw, hashed_pw, '3><7R45417') @@ -33,7 +33,10 @@ setup( 'mongokit', 'webob', 'wtforms', + 'py-bcrypt', + 'nose', ], + test_suite='nose.collector', license = 'AGPLv3', author = 'Christopher Webber', |