At least on Python 2.6, SSHAPasswordManager().checkPassword(hash, password) fails if hash is unicode, which it always is if stored in some databases. SSHAPasswordManager should encode the hash to utf-8 before trying to un-base64. zope.password is conspicuously missing a DelegatingPasswordManager that parses hashes in the form of {scheme}hash and uses getUtility(IPasswordManager, SCHEME) to check a variety of schemes. Thanks, Daniel Holth
On 2009-9-22 18:59, Daniel Holth wrote:
At least on Python 2.6, SSHAPasswordManager().checkPassword(hash, password) fails if hash is unicode, which it always is if stored in some databases. SSHAPasswordManager should encode the hash to utf-8 before trying to un-base64.
Isn't that a bug in those databases. The hash is not a unicode string but a bytestring. Wichert. -- Wichert Akkerman <wichert@wiggy.net> It is simple to make things. http://www.wiggy.net/ It is hard to make things simple.
sqlite for example will always return strings as unicode strings, or it will always return strings as byte strings. It doesn't know how to return the username colum as one kind of string and the hash column as another kind of string. I think this is really a bug in the urlsafe base64 module. This works: unicode(u"foobar".encode('base64')).decode('base64') On Wed, Sep 23, 2009 at 4:07 AM, Wichert Akkerman <wichert@wiggy.net> wrote:
On 2009-9-22 18:59, Daniel Holth wrote:
At least on Python 2.6, SSHAPasswordManager().checkPassword(hash, password) fails if hash is unicode, which it always is if stored in some databases. SSHAPasswordManager should encode the hash to utf-8 before trying to un-base64.
Isn't that a bug in those databases. The hash is not a unicode string but a bytestring.
Wichert.
-- Wichert Akkerman <wichert@wiggy.net> It is simple to make things. http://www.wiggy.net/ It is hard to make things simple.
On 2009-9-23 13:29, Daniel Holth wrote:
sqlite for example will always return strings as unicode strings, or it will always return strings as byte strings. It doesn't know how to return the username colum as one kind of string and the hash column as another kind of string.
SQLite is an extremely minimal database. This is just one of the situations where you need to massage its output. If you use SQLAlchemy on top of SQLite this will work properly.
I think this is really a bug in the urlsafe base64 module. This works: unicode(u"foobar".encode('base64')).decode('base64')
That generates something entirely differently than standard SSHA1 implementations, which will break interop. Wichert. -- Wichert Akkerman <wichert@wiggy.net> It is simple to make things. http://www.wiggy.net/ It is hard to make things simple.
You are right, it is an accident that unicode can be encoded to base64. It just annoys me that the Python 2.6 error for this is "TypeError: character mapping must return integer, None or unicode". In that case, how would you feel about updating PlainTextPasswordManager instead? It is not like the others because it won't work unless hash is a unicode PlainTextPasswordManager().encodePassword(u"password") => unicode PlainTextPasswordManager().checkPassword(unicode, unicode) and the others break unless hash is always a str. .encodePassword(u"password") => str .checkPassword(str, unicode) Here's my DelegatingPasswordManager. I might update it so .encodePassword() uses the unnamed IPasswordManager if installed. It would be nice if IPasswordManager included the name of the scheme to go between {} before the hash proper. class DelegatingPasswordManager(object): """Check passwords stored RFC 2307 style as {scheme}hash. Delegates to named IPasswordManager utilities.""" implements(IPasswordManager) SCHEME = u"Delegating Password Manager" _scheme = re.compile(r"^{(?P<scheme>[^}]*)}(?P<hash>.*)$") def encodePassword(self, password): raise NotImplementedError("DelegatingPasswordManager can only check passwords") def checkPassword(self, encoded_password, password): handler = None match = self._scheme.match(encoded_password) if not match: raise ValueError("Encoded password must be formatted as {scheme}hash.") try: scheme = match.group("scheme") handler = getUtility(IPasswordManager, scheme) except zope.component.interfaces.ComponentLookupError, e: raise LookupError("Handler for password encryption scheme {%s} not found." % scheme, e) if handler != self: return handler.checkPassword(encoded_password, password) return False
participants (2)
-
Daniel Holth -
Wichert Akkerman