[Zope-Checkins] CVS: Zope/lib/python/AccessControl - AuthEncoding.py:1.4 User.py:1.156
Shane Hathaway
shane@digicool.com
Thu, 13 Sep 2001 12:26:47 -0400
Update of /cvs-repository/Zope/lib/python/AccessControl
In directory cvs.zope.org:/tmp/cvs-serv12053
Modified Files:
AuthEncoding.py User.py
Log Message:
On Jeremy's suggestion, converted to the "SSHA" encryption scheme by default.
Added a framework for password digest schemes.
=== Zope/lib/python/AccessControl/AuthEncoding.py 1.3 => 1.4 ===
import sha, binascii
+from binascii import b2a_base64, a2b_base64
from string import upper
+from random import choice, randrange
+
+
+class PasswordEncryptionScheme: # An Interface
+
+ def encrypt(pw):
+ """
+ Encrypt the provided plain text password.
+ """
+
+ def validate(reference, attempt):
+ """
+ Validate the provided password string. Reference is the
+ correct password, which may be encrypted; attempt is clear text
+ password attempt.
+ """
+
+
+_schemes = []
+
+def registerScheme(id, s):
+ '''
+ Registers an LDAP password encoding scheme.
+ '''
+ _schemes.append((id, '{%s}' % id, s))
+
+def listSchemes():
+ r = []
+ for id, prefix, scheme in _schemes:
+ r.append(id)
+ return r
+
+
+class SSHADigestScheme:
+ '''
+ SSHA is a modification of the SHA digest scheme with a salt
+ starting at byte 20 of the base64-encoded string.
+ '''
+ # Source: http://developer.netscape.com/docs/technote/ldap/pass_sha.html
+
+ def generate_salt(self):
+ # Salt can be any length, but not more than about 37 characters
+ # because of limitations of the binascii module.
+ # 7 is what Netscape's example used and should be enough.
+ # All 256 characters are available.
+ salt = ''
+ for n in range(7):
+ salt += chr(randrange(256))
+ return salt
+
+ def encrypt(self, pw):
+ pw = str(pw)
+ salt = self.generate_salt()
+ return b2a_base64(sha.new(pw + salt).digest() + salt)[:-1]
+
+ def validate(self, reference, attempt):
+ try:
+ ref = a2b_base64(reference)
+ except binascii.Error:
+ # Not valid base64.
+ return 0
+ salt = ref[20:]
+ compare = b2a_base64(sha.new(attempt + salt).digest() + salt)[:-1]
+ return (compare == reference)
+
+registerScheme('SSHA', SSHADigestScheme())
+
+
+class SHADigestScheme:
+
+ def encrypt(self, pw):
+ return b2a_base64(sha.new(pw).digest())[:-1]
+
+ def validate(self, reference, attempt):
+ compare = b2a_base64(sha.new(attempt).digest())[:-1]
+ return (compare == reference)
+
+registerScheme('SHA', SHADigestScheme())
+
# Bogosity on various platforms due to ITAR restrictions
try:
- import crypt
+ from crypt import crypt
except ImportError:
crypt = None
-
+
+if crypt is not None:
+
+ class CryptDigestScheme:
+
+ def generate_salt(self):
+ choices = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789./")
+ return choice(choices) + choice(choices)
+
+ def encrypt(self, pw):
+ return crypt(pw, self.generate_salt())
+
+ def validate(self, reference, attempt):
+ a = crypt(attempt, reference[:2])
+ return (a == reference)
+
+ registerScheme('CRYPT', CryptDigestScheme())
+
def pw_validate(reference, attempt):
"""Validate the provided password string, which uses LDAP-style encoding
notation. Reference is the correct password, attempt is clear text
password attempt."""
-
- result = 0
- if upper(reference[:5]) == '{SHA}':
- attempt = binascii.b2a_base64(sha.new(attempt).digest())[:-1]
- result = reference[5:] == attempt
- elif upper(reference[:7]) == '{CRYPT}' and crypt is not None:
- #if crypt is None, it's not compiled in and everything will fail
- attempt = crypt.crypt(attempt, reference[7:9])
- result = reference[7:] == attempt
- else:
- result = reference == attempt
-
- return result
+ for id, prefix, scheme in _schemes:
+ lp = len(prefix)
+ if reference[:lp] == prefix:
+ return scheme.validate(reference[lp:], attempt)
+ # Assume cleartext.
+ return (reference == attempt)
def is_encrypted(pw):
- return pw[:5] == '{SHA}' or pw[:7] == '{CRYPT}'
+ for id, prefix, scheme in _schemes:
+ lp = len(prefix)
+ if pw[:lp] == prefix:
+ return 1
+ return 0
-def pw_encrypt(pw, encoding='SHA'):
+def pw_encrypt(pw, encoding='SSHA'):
"""Encrypt the provided plain text password using the encoding if provided
and return it in an LDAP-style representation."""
- if encoding == 'SHA':
- return '{SHA}' + binascii.b2a_base64(sha.new(pw).digest())[:-1]
- else:
- raise ValueError, 'Not supported: %s' % encoding
+ for id, prefix, scheme in _schemes:
+ if encoding == id:
+ return prefix + scheme.encrypt(pw)
+ raise ValueError, 'Not supported: %s' % encoding
pw_encode = pw_encrypt # backward compatibility
=== Zope/lib/python/AccessControl/User.py 1.155 => 1.156 ===
def _encryptPassword(self, pw):
- return AuthEncoding.pw_encrypt(pw, 'SHA')
+ return AuthEncoding.pw_encrypt(pw, 'SSHA')
def domainSpecValidate(self, spec):
for ob in spec: