diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2011-01-27 22:01:30 +0000 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2011-01-27 22:01:30 +0000 |
| commit | bc738f4c6e35a31c9edd5fb54f13773e15978a09 (patch) | |
| tree | f1509cb67e3b20e8ace7d3d30d7f2a119feb58d9 | |
| parent | a0f25630f6cf96d867b8a903c9f8c187c1261476 (diff) | |
| download | passlib-bc738f4c6e35a31c9edd5fb54f13773e15978a09.tar.gz | |
moved secret_chars to UTs; moved default/min/max_rounds to ExtCryptHandler; updated CryptHandler docs
| -rw-r--r-- | passlib/handler.py | 186 | ||||
| -rw-r--r-- | passlib/tests/handler_utils.py | 11 | ||||
| -rwxr-xr-x | passlib/tests/test_bcrypt.py | 1 | ||||
| -rw-r--r-- | passlib/tests/test_des_crypt.py | 2 | ||||
| -rw-r--r-- | passlib/unix/bcrypt.py | 4 | ||||
| -rw-r--r-- | passlib/unix/des_crypt.py | 6 | ||||
| -rw-r--r-- | passlib/unix/md5_crypt.py | 2 | ||||
| -rw-r--r-- | passlib/unix/sha_crypt.py | 2 | ||||
| -rw-r--r-- | passlib/unix/sun_md5_crypt.py | 2 |
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 |
