Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Removing bcrypt support

  • Loading branch information...
commit 02302fbc2f0f75aea7012b837716a025518753ba 1 parent 7c01700
@kudos authored
Showing with 29 additions and 67 deletions.
  1. +6 −2 README.md
  2. +16 −47 passwords/__init__.py
  3. +0 −1  requirements.txt
  4. +7 −17 tests.py
View
8 README.md
@@ -5,16 +5,20 @@ Passwords
What is it?
---
-Passwords is a Python library with a standard and simple interface to cryptographic hashing functions for passwords. Currently it supports and defautls to [PBKDF2](http://en.wikipedia.org/wiki/PBKDF2) along with optional support for [bcrypt](http://en.wikipedia.org/wiki/Bcrypt).
+Passwords is a Python library with a standard and simple interface to cryptographic hashing functions for passwords. It uses a bundled [PBKDF2](http://en.wikipedia.org/wiki/PBKDF2) to give it no external dependancies. Credit goes to Armin Ronacher for the algorithm [implementation](https://github.com/mitsuhiko/python-pbkdf2)
The strings generated contain all the information to recreate the hash from the original password. The algorithm used, the cost factor, the salt and the resulting password hash.
+Why?
+---
+This is an attempt to make it easier for developers to find and use a cryptographic library suitable for password storage.
+
Usage
---
import passwords
password = "god"
- password_hash = passwords.hash(password)
+ password_hash = passwords.crypt(password)
# '$pbkdf2-256-1$8$ndzIsQl2gH4R46d7BCzkWA1K909904c2fe5c48ea1b0bf64caf35663987e871628c5cdbfb'
passwords.verify(password, password_hash) # True
View
63 passwords/__init__.py
@@ -1,81 +1,52 @@
__title__ = 'passwords'
__version__ = '0.1.0'
-import pbkdf2
-import os
-import base64
-from itertools import izip
-
-try:
- import bcrypt
-except ImportError:
- pass
+from os import urandom
+from base64 import b64encode
+from itertools import izip
+import pbkdf2
-def hash(password, algorithm="pbkdf2", cost=8):
+def crypt(password, cost=2):
"""
Hash a password
- pbkdf2 sample:
+ result sample:
$pbkdf2-256-1$8$FRakfnkgpMjnqs1Xxgjiwgycdf68be9b06451039cc\
0f7075ec1c369fa36f055b1705ec7a
- bcrypt sample:
- $bcrypt-2a$08$9y1RbQ8Acdxq72Scf2ZqwuFSk9leg7Y.7E/lyYrDEjtN6kTIG4GKi
- The returned strings are broken down into
+ The returned string is broken down into
- The algorithm and version used
- The cost factor, number of iterations over the hash
- The salt
- The password
"""
- if algorithm == "bcrypt":
- salt = bcrypt.gensalt(cost)
- return bcrypt.hashpw(password, salt).replace("$2a$", "$bcrypt-2a$")
- elif algorithm == "pbkdf2":
- meta, salt = _generate_salt(cost)
+ salt = _generate_salt(cost)
- for iteration in range(0, min(max(cost, 4), 31)):
- password = pbkdf2.pbkdf2_hex(password, salt)
+ hashed = pbkdf2.pbkdf2_hex(password, salt, cost * 500)
- return meta + salt + password
+ return "$pbkdf2-256-1$" + str(cost) + "$" + salt + "$" + hashed
def verify(password, hash):
"""
Verify a password against a passed hash
"""
- head, algorithm, cost, salthash = hash.split("$")
-
- if algorithm == "pbkdf2-256-1":
+ _, algorithm, cost, salt, password_hash = hash.split("$")
- salt = salthash[:24]
+ password = pbkdf2.pbkdf2_hex(password, salt, int(cost) * 500)
- password_hash = salthash[24:]
-
- rehash = password
-
- for iteration in range(0, min(max(int(cost), 4), 31)):
- rehash = pbkdf2.pbkdf2_hex(rehash, salt)
-
- return _safe_str_cmp(rehash, password_hash)
-
- elif algorithm == "bcrypt-2a":
- # replace our algo identifier with bcrypt's internal one
- # for some reason bcrypt couples the algo information with
- # the salt rather than the completed hash
- hash = hash.replace("$bcrypt-2a$", "$2a$")
- salt = hash[:29]
- return _safe_str_cmp(bcrypt.hashpw(password, salt), hash)
+ return _safe_str_cmp(password, password_hash)
def _safe_str_cmp(a, b):
"""
+ Internal function to efficiently iterate over the hashes
+
Regular string compare will bail at the earliest opportunity
which allows timing attacks
-
- Efficiently iterate over the hashes
"""
if len(a) != len(b):
return False
@@ -86,6 +57,4 @@ def _safe_str_cmp(a, b):
def _generate_salt(cost):
- meta = "$pbkdf2-256-1$" + str(cost) + "$"
- # using 18 instead of bcrypt's 16 because base64 is going to pad it anyway
- return meta, base64.b64encode(os.urandom(18))
+ return b64encode(urandom(18))
View
1  requirements.txt
@@ -1,2 +1 @@
nose
-py-bcrypt
View
24 tests.py
@@ -1,21 +1,11 @@
-from passwords import hash, verify
+from passwords import crypt, verify
-def test_pbkdf2_valid():
- hashed = hash('test1')
- assert verify('test1', hashed)
+def test_valid():
+ hashed = crypt('password')
+ assert verify('password', hashed)
-def test_pbkdf2_invalid():
- hashed = hash('test1')
- assert not verify('test2', hashed)
-
-
-def test_bcrypt_valid():
- hashed = hash('test1', "bcrypt", 12)
- assert verify('test1', hashed)
-
-
-def test_bcrypt_invalid():
- hashed = hash('test1', "bcrypt", 12)
- assert not verify('test2', hashed)
+def test_invalid():
+ hashed = crypt('password')
+ assert not verify('password2', hashed)
Please sign in to comment.
Something went wrong with that request. Please try again.