Skip to content

Commit

Permalink
Port AccessControl.AuthEncoding.MySQLDigestScheme to zope.password.
Browse files Browse the repository at this point in the history
This is very much a legacy scheme, encoding to a very weak 16 bit hash with no salt support.
  • Loading branch information
Martijn Pieters committed Feb 20, 2011
1 parent 86d125e commit 7739431
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGES.txt
Expand Up @@ -15,6 +15,9 @@ CHANGES
- Add a crypt password manager to fully support all methods named in RFC 2307.
It is contained in the 'legacy' module however, to flag crypt's status.

- Add a MySQL PASSWORD() (versions before 4.1) password manager, as also found
in Zope2's AccessControl.AuthEncoding module.

3.6.1 (2010-05-27)
------------------

Expand Down
6 changes: 6 additions & 0 deletions src/zope/password/configure.zcml
Expand Up @@ -27,6 +27,12 @@
factory=".password.SSHAPasswordManager"
/>

<utility
name="MYSQL"
provides=".interfaces.IPasswordManager"
factory=".legacy.MySQLPasswordManager"
/>

<configure zcml:condition="installed crypt">
<utility
name="Crypt"
Expand Down
70 changes: 70 additions & 0 deletions src/zope/password/legacy.py
Expand Up @@ -112,3 +112,73 @@ def checkPassword(self, encoded_password, password):
def match(self, encoded_password):
return encoded_password.startswith('{CRYPT}')

class MySQLPasswordManager(object):
"""A MySQL digest manager.
This Password Manager implements the digest scheme as implemented in the
MySQL PASSWORD function in MySQL versions before 4.1. Note that this
method results in a very weak 16-byte hash.
>>> from zope.interface.verify import verifyObject
>>> manager = MySQLPasswordManager()
>>> verifyObject(IPasswordManager, manager)
True
>>> password = u"right \N{CYRILLIC CAPITAL LETTER A}"
>>> encoded = manager.encodePassword(password)
>>> encoded
'{MYSQL}0ecd752c5097d395'
>>> manager.match(encoded)
True
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
False
Using the password 'PHP & Information Security' should result in the
hash ``379693e271cd3bd6``, according to
http://phpsec.org/articles/2005/password-hashing.html
Our password manager generates the same value when seeded with the, so we
can be sure, our output is compatible with MySQL versions before 4.1::
>>> password = 'PHP & Information Security'
>>> encoded = manager.encodePassword(password)
>>> encoded
'{MYSQL}379693e271cd3bd6'
>>> manager.checkPassword(encoded, password)
True
>>> manager.checkPassword(encoded, password + u"wrong")
False
The manager only claims to implement MYSQL encodings, anything not
starting with the string {MYSQL} returns False::
>>> manager.match('{MD5}someotherhash')
False
"""

implements(IPasswordManager)

def encodePassword(self, password):
nr = 1345345333L
add = 7
nr2 = 0x12345671L
for i in _encoder(password)[0]:
if i == ' ' or i == '\t':
continue
nr ^= (((nr & 63) + add) * ord(i)) + (nr << 8)
nr2 += (nr2 << 8) ^ nr
add += ord(i)
r0 = nr & ((1L << 31) - 1L)
r1 = nr2 & ((1L << 31) - 1L)
return "{MYSQL}%08lx%08lx" % (r0, r1)

def checkPassword(self, encoded_password, password):
return encoded_password == self.encodePassword(password)

def match(self, encoded_password):
return encoded_password.startswith('{MYSQL}')
6 changes: 6 additions & 0 deletions src/zope/password/testing.py
Expand Up @@ -23,6 +23,7 @@
from zope.password.password import MD5PasswordManager
from zope.password.password import SHA1PasswordManager
from zope.password.password import SSHAPasswordManager
from zope.password.legacy import MySQLPasswordManager
from zope.password.vocabulary import PasswordManagerNamesVocabulary

try:
Expand All @@ -45,6 +46,8 @@ def setUpPasswordManagers():
<zope.password.password.MD5PasswordManager object at 0x...>
>>> getUtility(IPasswordManager, 'SHA1')
<zope.password.password.SHA1PasswordManager object at 0x...>
>>> getUtility(IPasswordManager, 'MYSQL')
<zope.password.legacy.MySQLPasswordManager object at 0x...>
>>> try:
... import crypt
Expand All @@ -68,6 +71,8 @@ def setUpPasswordManagers():
True
>>> 'MD5' in voc
True
>>> 'MYSQL' in voc
True
>>> CryptPasswordManager is None or 'Crypt' in voc
True
Expand All @@ -77,6 +82,7 @@ def setUpPasswordManagers():
provideUtility(SSHAPasswordManager(), IPasswordManager, 'SSHA')
provideUtility(MD5PasswordManager(), IPasswordManager, 'MD5')
provideUtility(SHA1PasswordManager(), IPasswordManager, 'SHA1')
provideUtility(MySQLPasswordManager(), IPasswordManager, 'MYSQL')

if CryptPasswordManager is not None:
provideUtility(CryptPasswordManager, IPasswordManager, 'Crypt')
Expand Down

0 comments on commit 7739431

Please sign in to comment.