summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2011-01-27 22:01:30 +0000
committerEli Collins <elic@assurancetechnologies.com>2011-01-27 22:01:30 +0000
commitbc738f4c6e35a31c9edd5fb54f13773e15978a09 (patch)
treef1509cb67e3b20e8ace7d3d30d7f2a119feb58d9
parenta0f25630f6cf96d867b8a903c9f8c187c1261476 (diff)
downloadpasslib-bc738f4c6e35a31c9edd5fb54f13773e15978a09.tar.gz
moved secret_chars to UTs; moved default/min/max_rounds to ExtCryptHandler; updated CryptHandler docs
-rw-r--r--passlib/handler.py186
-rw-r--r--passlib/tests/handler_utils.py11
-rwxr-xr-xpasslib/tests/test_bcrypt.py1
-rw-r--r--passlib/tests/test_des_crypt.py2
-rw-r--r--passlib/unix/bcrypt.py4
-rw-r--r--passlib/unix/des_crypt.py6
-rw-r--r--passlib/unix/md5_crypt.py2
-rw-r--r--passlib/unix/sha_crypt.py2
-rw-r--r--passlib/unix/sun_md5_crypt.py2
9 files changed, 108 insertions, 108 deletions
diff --git a/passlib/handler.py b/passlib/handler.py
index 3b53b49..79e5125 100644
--- a/passlib/handler.py
+++ b/passlib/handler.py
@@ -107,119 +107,82 @@ def is_ext_crypt_handler(obj):
class CryptHandler(object):
"""base class for implementing a password algorithm.
- The following should be filled out for all crypt algorithm subclasses.
- Additional methods, attributes, and features may vary.
+ Overview
+ ========
+ All passlib-compatible password hash handlers must follow
+ the interface specified by this class.
- Informational Attributes
- ========================
- .. attribute:: name
-
- This should be a globally unique name to identify
- the hash algorithm with.
-
- .. attribute:: salt_bytes
-
- This is a purely informational attribute
- listing how many bytes are in the salt your algorithm uses.
+ Frontend Interface
+ ==================
+ The following 3 methods should be implemented for all classes,
+ and provide a simple interface for users wishes
+ to quickly manipulation passwords and hashes:
- .. attribute:: hash_bytes
-
- This is a purely informational attribute
- listing how many bytes are in the cheksum part of your algorithm's hash.
-
- .. note::
-
- Note that all the bit counts should measure
- the number of bits of entropy, not the number of bits
- a given encoding takes up.
-
- .. attribute:: has_salt
-
- This is a virtual attribute,
- calculated based on the value of the salt_bytes attribute.
- It returns ``True`` if the algorithm contains any salt bytes,
- else ``False``.
+ .. automethod:: encrypt
+ .. automethod:: verify
+ .. automethod:: identify
- .. attribute:: secret_chars
+ Backend Interface
+ =================
+ The frontend for most handlers is built upon the following
+ backend methods, whose semantics are closer to the traditional unix crypt
+ interface, but require the user's code to have more
+ knowledge of the specific algorithm in use.
- Number of characters in secret which are used.
- If ``None`` (the default), all chars are used.
- BCrypt, for example, only uses the first 55 chars.
+ .. automethod:: genconfig
+ .. automethod:: genhash
- .. attribute:: has_rounds
- This is a purely informational attribute
- listing whether the algorithm can be scaled
- by increasing the number of rounds it contains.
- It is not required (defaults to False).
+ Informational Attributes
+ ========================
+ .. attribute:: name
- .. attribute:: has_named_rounds
+ A unique name used to identify
+ the particular algorithm this handler implements.
- If this flag is true, then the algorithm's
- encrypt method supports a ``rounds`` keyword
- which (at the very least) accepts the following
- strings as possible values:
+ These names should consist only of lowercase a-z, the digits 0-9, and underscores.
- * ``fast`` -- number of rounds will be selected
- to provide adequate security for most user accounts.
- This is retuned perodically to take around .25 seconds.
+ Examples: ``"des_crypt"``, ``"md5_crypt"``.
- * ``medium`` -- number of rounds will be selected
- to provide adequate security for most root/administrative accounts
- This is retuned perodically to take around .75 seconds.
+ .. attribute:: setting_kwds
- * ``slow`` -- number of rounds will be selected
- to require a large amount of calculation time.
- This is retuned perodically to take around 1.5 seconds.
+ If the algorithm supports per-hash configuration
+ (such as salts, variable rounds, etc), this attribute
+ should contain a tuple of keywords corresponding
+ to each of those configuration options.
- .. note::
- Last retuning of the default round sizes was done
- on 2009-07-06 using a 2ghz system.
+ This should correspond with the keywords accepted
+ by that algorithm's :meth:`genconfig` method,
+ see that method for details.
- Common Methods
- ==============
- .. automethod:: identify
+ If no settings are supported, this attribute
+ should be an empty tuple.
- .. automethod:: encrypt
+ .. attribute:: context_kwds
- .. automethod:: verify
+ Some algorithms require external contextual information
+ in order to generate a checksum for a password.
+ An example of this is postgres' md5 algorithm,
+ which requires the username to use as a salt.
- Implementing a new crypt algorithm
- ==================================
- Subclass this class, and implement :meth:`identify`
- and :meth:`encrypt` so that they implement your
- algorithm according to it's documentation
- and the specifications of the methods themselves.
- You must also specify :attr:``name``.
- Optionally, you may override :meth:`verify`
- and set various informational attributes.
+ This attribute should contain a tuple of keywords
+ which should be passed into :meth:`encrypt`, :meth:`verify`,
+ and :meth:`genhash` in order to encrypt a password.
+ Since most password hashes require no external information,
+ this tuple will usually be empty.
"""
#=========================================================
#class attrs
#=========================================================
- #---------------------------------------------------------
- #registry
- #---------------------------------------------------------
name = None #globally unique name to identify algorithm. should be lower case and hyphens only
aliases = () #optional list of aliases (other names) this hash should be recognized by
+
context_kwds = () #tuple of additional kwds required for any encrypt / verify operations; eg "realm" or "user"
setting_kwds = () #tuple of additional kwds that encrypt accepts for configuration algorithm; eg "salt" or "rounds"
- #---------------------------------------------------------
- #optional informational attributes
- #---------------------------------------------------------
- secret_chars = -1 #max number of chars of secret that are used in hash. -1 if all chars used.
-
- #---------------------------------------------------------
- #algorithm rounds information - only required if alg supports rounds
- #---------------------------------------------------------
- default_rounds = None #default number of rounds to use if none specified (can be name of a preset)
- min_rounds = None #minimum number of rounds (smaller values silently ignored)
- max_rounds = None #maximum number of rounds (larger values silently ignored)
-
#=========================================================
#primary interface - primary methods implemented by each handler
#=========================================================
@@ -228,10 +191,14 @@ class CryptHandler(object):
def genhash(cls, secret, config, **context_kwds):
"""encrypt secret to hash
+ Overview
+ ========
takes in a password, optional configuration string,
and any required contextual information the algorithm needs,
and returns the encoded hash strings.
+ Call Syntax
+ ===========
:arg secret: string containing the password to be encrypted
:arg config:
configuration string to use when encrypting secret.
@@ -261,9 +228,43 @@ class CryptHandler(object):
def genconfig(cls, **settings):
"""return configuration string encoding settings for hash generation
- Many hashes have configuration options,
- and support a configuration string which encodes them.
- (This is usually an abbreviated version of their encoded hash format, sans the actual checksum).
+ Overview
+ ========
+ Many hashes have configuration options, and support a format
+ which encodes them into a single configuration string.
+ (This configuration string is usually an abbreviated version of their
+ encoded hash format, sans the actual checksum, and is commonly
+ referred to as a ``salt string``, though it may contain much more
+ than just a salt).
+
+ This function takes in optional configuration options (a complete list
+ of which should be found in :attr:`setting_kwds`), validates
+ the inputs, fills in defaults where appropriate, and returns
+ a configuration string.
+
+ For algorithms which do not have any configuration options,
+ this function should always return ``None``.
+
+ While each algorithm may have it's own configuration options,
+ the following keywords (if supported) should always have a consistent
+ meaning:
+
+ * ``salt`` - algorithm uses a salt. if passed into genconfig,
+ should contain an encoded salt string of length and character set
+ required by the specific handler.
+
+ salt strings which are too small or have invalid characters
+ should cause an error, salt strings which are too large
+ should be truncated but accepted.
+
+ * ``rounds`` - algorithm uses a variable number of rounds. if passed
+ into genconfig, should contain an integer number of rounds
+ (this may represent logarithmic rounds, eg bcrypt, or linear, eg sha-crypt).
+ if the number of rounds is too small or too large, it should
+ be clipped but accepted.
+
+ Call Syntax
+ ===========
:param settings:
this function takes in keywords as specified in :attr:`setting_kwds`.
@@ -279,7 +280,7 @@ class CryptHandler(object):
:returns:
the configuration string, or ``None`` if the algorithm does not support any configuration options.
"""
- #NOTE: this implements a default method suitable ONLY for classes with no configuration.
+ #NOTE: this implements a default method which is suitable ONLY for classes with no configuration.
if cls.setting_kwds:
raise NotImplementedError, "classes with config kwds must implement genconfig()"
if settings:
@@ -463,6 +464,13 @@ class ExtCryptHandler(CryptHandler):
def min_salt_chars(cls):
return cls.salt_chars
+ #---------------------------------------------------------
+ #_norm_rounds() configuration
+ #---------------------------------------------------------
+ default_rounds = None #default number of rounds to use if none specified (can be name of a preset)
+ min_rounds = None #minimum number of rounds (smaller values silently ignored)
+ max_rounds = None #maximum number of rounds (larger values silently ignored)
+
#=========================================================
#backend parsing routines - used by helpers below
#=========================================================
diff --git a/passlib/tests/handler_utils.py b/passlib/tests/handler_utils.py
index 7f338a0..fb92c4e 100644
--- a/passlib/tests/handler_utils.py
+++ b/passlib/tests/handler_utils.py
@@ -33,6 +33,10 @@ class _HandlerTestCase(TestCase):
#NOTE: would like unicode support for all hashes. until then, this flag is set for those which aren't.
supports_unicode = False
+ #maximum number of chars which hash will include in checksum
+ #override this only if hash doesn't use all chars (the default)
+ secret_chars = -1
+
#list of (secret,hash) pairs which handler should verify as matching
known_correct = []
@@ -107,9 +111,6 @@ class _HandlerTestCase(TestCase):
self.assert_(name.lower() == name, "name not lower-case:")
self.assert_(re.match("^[a-z0-9-]+$", name), "name must be alphanum + hyphen:")
- value = ga("secret_chars")
- self.assert_(value is not None and (value == -1 or value > 0), "secret_chars must be -1 or positive integer")
-
#=========================================================
#identify
#=========================================================
@@ -232,8 +233,8 @@ class _HandlerTestCase(TestCase):
#test secret handling
#---------------------------------------------------------
def test_37_secret_chars(self):
- "test secret_chars limitation"
- sc = self.handler.secret_chars
+ "test secret_chars limit"
+ sc = self.secret_chars
base = "too many secrets"
alt = 'x' #char that's not in base string
diff --git a/passlib/tests/test_bcrypt.py b/passlib/tests/test_bcrypt.py
index deb3765..78abea2 100755
--- a/passlib/tests/test_bcrypt.py
+++ b/passlib/tests/test_bcrypt.py
@@ -220,6 +220,7 @@ if pybcrypt and enable_test("backends"):
#=========================================================
class BCryptTest(_HandlerTestCase):
handler = mod.BCrypt
+ secret_chars = 72
known_correct = (
#selected subset of backend test vectors (see above)
diff --git a/passlib/tests/test_des_crypt.py b/passlib/tests/test_des_crypt.py
index 6553a77..b0b6965 100644
--- a/passlib/tests/test_des_crypt.py
+++ b/passlib/tests/test_des_crypt.py
@@ -21,6 +21,8 @@ log = getLogger(__name__)
class DesCryptTest(_HandlerTestCase):
"test DesCrypt algorithm"
handler = mod.DesCrypt
+ secret_chars = 8
+
known_correct = (
#secret, example hash which matches secret
('', 'OgAwTx2l6NADI'),
diff --git a/passlib/unix/bcrypt.py b/passlib/unix/bcrypt.py
index 68ba729..cf1a5af 100644
--- a/passlib/unix/bcrypt.py
+++ b/passlib/unix/bcrypt.py
@@ -69,12 +69,10 @@ class BCrypt(ExtCryptHandler):
#algorithm info
#=========================================================
name = "bcrypt"
- #stats: 192 bit checksum, 128 bit salt, 2**(4..31) rounds
+ #stats: 192 bit checksum, 128 bit salt, 2**(4..31) rounds, max 72 chars of secret
setting_kwds = ("salt", "rounds")
- secret_chars = 72
-
salt_chars = 22
default_rounds = 12
diff --git a/passlib/unix/des_crypt.py b/passlib/unix/des_crypt.py
index d9c941a..8fa1601 100644
--- a/passlib/unix/des_crypt.py
+++ b/passlib/unix/des_crypt.py
@@ -89,12 +89,10 @@ class DesCrypt(ExtCryptHandler):
name = "des-crypt"
aliases = ("unix-crypt",)
- #stats: 66 bit checksum, 12 bit salt
+ #stats: 66 bit checksum, 12 bit salt, max 8 chars of secret
setting_kwds = ("salt")
- secret_chars = 8
-
salt_chars = 2
#=========================================================
@@ -175,8 +173,6 @@ class ExtDesCrypt(ExtCryptHandler):
setting_kwds = ("salt", "rounds")
- secret_chars = -1
-
salt_chars = 4
#NOTE: this has variable rounds, but it's so old we just max them out by default
diff --git a/passlib/unix/md5_crypt.py b/passlib/unix/md5_crypt.py
index 855b667..c46f4e9 100644
--- a/passlib/unix/md5_crypt.py
+++ b/passlib/unix/md5_crypt.py
@@ -157,8 +157,6 @@ class Md5Crypt(ExtCryptHandler):
setting_kwds = ("salt",)
- secret_chars = -1
-
salt_chars = 8
min_salt_chars = 0
diff --git a/passlib/unix/sha_crypt.py b/passlib/unix/sha_crypt.py
index 9791cdf..ff01599 100644
--- a/passlib/unix/sha_crypt.py
+++ b/passlib/unix/sha_crypt.py
@@ -275,8 +275,6 @@ class _ShaCrypt(ExtCryptHandler):
#name - provided by subclass
setting_kwds = ("salt", "rounds")
- secret_chars = -1
-
min_salt_chars = 0
salt_chars = 16
diff --git a/passlib/unix/sun_md5_crypt.py b/passlib/unix/sun_md5_crypt.py
index fe51abc..6fba453 100644
--- a/passlib/unix/sun_md5_crypt.py
+++ b/passlib/unix/sun_md5_crypt.py
@@ -188,8 +188,6 @@ class SunMd5Crypt(ExtCryptHandler):
setting_kwds = ("salt","rounds")
- secret_chars = -1
-
salt_chars = 8
min_salt_chars = 0