[Checkins] SVN: zope.password/trunk/ Port AccessControl.AuthEncoding.MySQLDigestScheme to zope.password.
Martijn Pieters
mj at zopatista.com
Sun Feb 20 08:59:02 EST 2011
Log message for revision 120462:
Port AccessControl.AuthEncoding.MySQLDigestScheme to zope.password.
This is very much a legacy scheme, encoding to a very weak 16 bit hash with no salt support.
Changed:
U zope.password/trunk/CHANGES.txt
U zope.password/trunk/src/zope/password/configure.zcml
U zope.password/trunk/src/zope/password/legacy.py
U zope.password/trunk/src/zope/password/testing.py
-=-
Modified: zope.password/trunk/CHANGES.txt
===================================================================
--- zope.password/trunk/CHANGES.txt 2011-02-20 13:37:08 UTC (rev 120461)
+++ zope.password/trunk/CHANGES.txt 2011-02-20 13:59:02 UTC (rev 120462)
@@ -15,6 +15,9 @@
- 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)
------------------
Modified: zope.password/trunk/src/zope/password/configure.zcml
===================================================================
--- zope.password/trunk/src/zope/password/configure.zcml 2011-02-20 13:37:08 UTC (rev 120461)
+++ zope.password/trunk/src/zope/password/configure.zcml 2011-02-20 13:59:02 UTC (rev 120462)
@@ -27,6 +27,12 @@
factory=".password.SSHAPasswordManager"
/>
+ <utility
+ name="MYSQL"
+ provides=".interfaces.IPasswordManager"
+ factory=".legacy.MySQLPasswordManager"
+ />
+
<configure zcml:condition="installed crypt">
<utility
name="Crypt"
Modified: zope.password/trunk/src/zope/password/legacy.py
===================================================================
--- zope.password/trunk/src/zope/password/legacy.py 2011-02-20 13:37:08 UTC (rev 120461)
+++ zope.password/trunk/src/zope/password/legacy.py 2011-02-20 13:59:02 UTC (rev 120462)
@@ -112,3 +112,73 @@
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}')
Modified: zope.password/trunk/src/zope/password/testing.py
===================================================================
--- zope.password/trunk/src/zope/password/testing.py 2011-02-20 13:37:08 UTC (rev 120461)
+++ zope.password/trunk/src/zope/password/testing.py 2011-02-20 13:59:02 UTC (rev 120462)
@@ -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:
@@ -45,6 +46,8 @@
<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
@@ -68,6 +71,8 @@
True
>>> 'MD5' in voc
True
+ >>> 'MYSQL' in voc
+ True
>>> CryptPasswordManager is None or 'Crypt' in voc
True
@@ -77,6 +82,7 @@
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')
More information about the checkins
mailing list