Skip to content

Commit

Permalink
Remove the 'cosmetic' salt from the SHA1 implementation as well, upda…
Browse files Browse the repository at this point in the history
…te docs.
  • Loading branch information
Martijn Pieters committed Feb 20, 2011
1 parent 6f0e2be commit 7e7e2eb
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 33 deletions.
9 changes: 5 additions & 4 deletions CHANGES.txt
Expand Up @@ -19,10 +19,11 @@ CHANGES
- Add a MySQL PASSWORD() (versions before 4.1) password manager, as also found
in Zope2's AccessControl.AuthEncoding module.

- Remove the useless, cosmetic salt from the MD5 password manager. This makes
this manager compatible with other MD5 hash implementations such as RFC 2307
but doesn't lower it's security in any way. Old, still 'salted' password
hashes are still supported.
- Remove the useless, cosmetic salt from the MD5 and SHA1 password managers.
This makes the output of these managers compatible with other MD5 and SHA1
hash implementations such as RFC 2307 but doesn't lower it's security in any
way. Checking passwards against old, still 'salted' password hashes is still
supported.

3.6.1 (2010-05-27)
------------------
Expand Down
6 changes: 3 additions & 3 deletions README.txt
Expand Up @@ -13,11 +13,11 @@ six implementations:
more secure implementations.

* MD5PasswordManager - a password manager that uses MD5 algorithm to
encode passwords. It's generally weak against dictionary attacks.
encode passwords. It's generally weak against dictionary attacks due to a
lack of a salt.

* SHA1PasswordManager - a password manager that uses SHA1 algorithm to
encode passwords. It has the same salt weakness as the
MD5PasswordManager.
encode passwords. It has the same weakness as the MD5PasswordManager.

* SSHAPasswordManager - the most secure password manager that is
strong against dictionary attacks. It's basically SHA1-encoding
Expand Down
41 changes: 15 additions & 26 deletions src/zope/password/password.py
Expand Up @@ -18,7 +18,6 @@
from base64 import urlsafe_b64encode
from base64 import urlsafe_b64decode
from os import urandom
from random import randint
from codecs import getencoder
try:
from hashlib import md5, sha1
Expand Down Expand Up @@ -227,17 +226,14 @@ def match(self, encoded_password):
class SHA1PasswordManager(PlainTextPasswordManager):
"""SHA1 password manager.
Note: use of salt in this password manager is purely
cosmetical. Use SSHA if you want increased security.
>>> from zope.interface.verify import verifyObject
>>> manager = SHA1PasswordManager()
>>> verifyObject(IMatchingPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
>>> encoded = manager.encodePassword(password, salt="")
>>> encoded = manager.encodePassword(password)
>>> encoded
'{SHA}04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
>>> manager.match(encoded)
Expand All @@ -247,23 +243,10 @@ class SHA1PasswordManager(PlainTextPasswordManager):
>>> manager.checkPassword(encoded, password + u"wrong")
False
>>> encoded = manager.encodePassword(password)
>>> encoded[-40:]
'04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
>>> manager.match(encoded)
True
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
False
>>> manager.encodePassword(password) != manager.encodePassword(password)
True
The old version of this password manager didn't add the {SHA} to
passwords. Let's check if it can work with old stored passwords.
>>> encoded = manager.encodePassword(password, salt="")
>>> encoded = manager.encodePassword(password)
>>> encoded = encoded[5:]
>>> encoded
'04b4eec7154c5f3a2ec6d2956fb80b80dc737402'
Expand All @@ -277,6 +260,14 @@ class SHA1PasswordManager(PlainTextPasswordManager):
>>> manager.match(encoded)
False
A previous version of this manager also created a cosmetic salt, added
to the start of the hash, but otherwise not used in creating the hash
itself. To still support these 'hashed' passwords, only the last 40 bytes
of the pre-existing hash are used:
>>> manager.checkPassword('salt' + encoded, password)
True
Previously, this password manager used {SHA1} as a prefix, but this was
changed to be compatible with LDAP (RFC 2307). The old prefix is still
supported:
Expand All @@ -293,17 +284,15 @@ class SHA1PasswordManager(PlainTextPasswordManager):
"""

def encodePassword(self, password, salt=None):
if salt is None:
salt = "%08x" % randint(0, 0xffffffff)
return '{SHA}%s%s' % (salt, sha1(_encoder(password)[0]).hexdigest())
# The salt argument only exists for backwards compatibility and is
# ignored on purpose.
return '{SHA}%s' % sha1(_encoder(password)[0]).hexdigest()

def checkPassword(self, encoded_password, password):
if self.match(encoded_password):
encoded = encoded_password[encoded_password.find('}') + 1:]
salt = encoded[:-40]
return encoded == self.encodePassword(password, salt)[5:]
salt = encoded_password[:-40]
return encoded_password == self.encodePassword(password, salt)[5:]
return encoded[-40:] == self.encodePassword(password)[5:]
return encoded_password[-40:] == self.encodePassword(password)[5:]

def match(self, encoded_password):
return (
Expand Down

0 comments on commit 7e7e2eb

Please sign in to comment.