from django.contrib.auth.hashers import BasePasswordHasher from collections import OrderedDict from django.utils.translation import ugettext_noop as _ from django.utils.crypto import get_random_string import hashlib class UserCakePasswordHasher(BasePasswordHasher): """ Authenticate against Usercake passwords in Django. (http://usercake.com/) Use this class to authenticate against Usercake password strings. When importing passwords from Usercake, the database values should be prefixed with "usercake$". Usercake passwords consist of a 65 character string. The first 25 characters are the salt The next block of 40 characters is the password encrypted using Sha1 and the salt h = UserCakePasswordHasher() h.verify("123456789", "usercake$860b4cefa917c430ed85d89525e0158d5be9e1515333a9dcfefd51a2419a119d1") >>>>True r = h.encode("123456789") h.verify("123456789", r) >>>>True """ algorithm = "usercake" def _apply_hash(self, salt, password): return hashlib.sha1( salt + password ).hexdigest() def _mask_hash(self, hash, show=6, char="*"): """ Returns the given hash, with only the first ``show`` number shown. The rest are masked with ``char`` for security reasons. """ masked = hash[:show] masked += char * len(hash[show:]) return masked def salt(self): return get_random_string(25) def encode(self, password, salt=None): if not salt: salt = self.salt() assert len(salt) == 25 encoded_hash = self._apply_hash(salt, password) return self.algorithm + "$" + salt + encoded_hash def verify(self, password, encoded): algorithm, encoded = encoded.split("$", 1) assert algorithm == self.algorithm encoded_salt = encoded[:25] encoded_pass = encoded[25:] password_hash = self._apply_hash(encoded_salt, password) return password_hash == encoded_pass def safe_summary(self, encoded): algorithm, encoded = encoded.split("$", 1) assert algorithm == self.algorithm encoded_salt = encoded[:25] encoded_pass = encoded[25:] return OrderedDict([ (_('algorithm'), self.algorithm), (_('iterations'), "0"), (_('salt'), self._mask_hash( encoded_salt )), (_('hash'), self._mask_hash( encoded_pass )), ]) def must_update(self, encoded): """ Forces usercake passwords to be updated to a more secure algorithm """ algorithm, encoded = encoded.split("$", 1) if algorithm == self.algorithm: return True return False