This module is primarily used as an internal support module. It’s interface has not been finalized yet, and may be changed somewhat between major releases of Passlib, as the internal code is cleaned up and simplified.
This module, and the instructions on how to write a custom handler, definitely need to be rewritten for clarity. They are not yet organized, and may leave out some important details.
All that is required in order to write a custom handler that will work with Passlib is to create an object (be it module, class, or object) that exposes the functions and attributes required by the Password Hash Interface. For classes, Passlib does not make any requirements about what a class instance should look like (if the implementation even uses them).
That said, most of the handlers built into Passlib are based around the GenericHandler class, and it’s associated mixin classes. While deriving from this class is not required, doing so will greatly reduce the amount of additional code that is needed for all but the most convoluted password hash schemes.
Once a handler has been written, it may be used explicitly, passed into a CryptContext constructor, or registered globally with Passlib via the passlib.registry module.
Testing Hash Handlers for details about how to test custom handlers against Passlib’s unittest suite.
Most of the handlers built into Passlib are based around the GenericHandler class. This class is designed under the assumption that the common workflow for hashes is some combination of the following:
With this in mind, GenericHandler provides implementations of most of the Password Hash Interface methods, eliminating the need for almost all the boilerplate associated with writing a password hash.
In order to minimize the amount of unneeded features that must be loaded in, the GenericHandler class itself contains only the parts which are needed by almost all handlers: parsing, rendering, and checksum validation. Validation of all other parameters (such as salt, rounds, etc) is split out into separate mixin classes which enhance GenericHandler with additional features.
In order to use GenericHandler, just subclass it, and then do the following:
fill out the name attribute with the name of your hash.
fill out the setting_kwds attribute with a tuple listing all the settings your hash accepts.
provide an implementation of the from_string() classmethod.
this method should take in a potential hash string, parse it into components, and return an instance of the class which contains the parsed components. It should throw a ValueError if no hash, or an invalid hash, is provided.
provide an implementation of the to_string() instance method.
this method should render an instance of your handler class (such as returned by from_string()), returning a hash string.
provide an implementation of the _calc_checksum() instance method.
this is the heart of the hash; this method should take in the password as the first argument, then generate and return the digest portion of the hash, according to the settings (such as salt, etc) stored in the parsed instance this method was called from.
note that it should not return the full hash with identifiers, etc; that job should be performed by to_string().
Some additional notes:
- In addition to simply subclassing GenericHandler, most handlers will also benefit from adding in some of the mixin classes that are designed to add features to GenericHandler. See GenericHandler Mixins for more details.
- Most implementations will want to alter/override the default identify() method. By default, it returns True for all hashes that from_string() can parse without raising a ValueError; which is reliable, but somewhat slow. For faster identification purposes, subclasses may fill in the ident attribute with the hash’s identifying prefix, which identify() will then test for instead of calling from_string(). For more complex situations, a custom implementation should be used; the HasManyIdents mixin may also be helpful.
- This class does not support context kwds of any type, since that is a rare enough requirement inside passlib.
helper class for implementing hash handlers.
GenericHandler-derived classes will have (at least) the following constructor options, though others may be added by mixins and by the class itself:
[optional] If this attribute is filled in, the default identify() method will use it as a identifying prefix that can be used to recognize instances of this handler’s hash. Filling this out is recommended for speed.
This should be a unicode str.
This should be a unique regex object.
[optional] Specifies the number of characters that should be expected in the checksum string. If omitted, no check will be performed.
[optional] A string listing all the characters allowed in the checksum string. If omitted, no check will be performed.
This should be a unicode str.
[optional] If specified, hashes with this checksum will have their checksum normalized to None, treating it like a config string. This is mainly used by hash formats which don’t have a concept of a config string, so a unlikely-to-occur checksum (e.g. all zeros) is used by some implementations.
This should be a string of the same datatype as checksum, or None.
The following methods must be provided by handler subclass:
return parsed instance from hash/configuration string
|Parameters:||**context – context keywords to pass to constructor (if applicable).|
|if hash is incorrectly formatted|
|Returns:||hash parsed into components, for formatting / calculating checksum.|
render instance to hash or configuration string
|Returns:||if checksum is set, should return full hash string.
if not, should either return abbreviated configuration string,
or fill in a stub checksum.
should return native string type (ascii-bytes under python 2, unicode under python 3)
given secret; calcuate and return encoded checksum portion of hash string, taking config from object state
calc checksum implementations may assume secret is always either unicode or bytes, checks are performed by verify/etc.
The following methods have default implementations that should work for most cases, though they may be overridden if the hash subclass needs to:
validates checksum keyword against class requirements, returns normalized version of checksum.
mixin for validating salts.
This GenericHandler mixin adds a salt keyword to the class constuctor; any value provided is passed through the _norm_salt() method, which takes care of validating salt length and content, as well as generating new salts if one it not provided.
In order for _norm_salt() to do it’s job, the following attributes should be provided by the handler subclass:
The minimum number of characters allowed in a salt string. An ValueError will be throw if the provided salt is too small. Defaults to None, for no minimum.
The maximum number of characters allowed in a salt string. By default an ValueError will be throw if the provided salt is too large; but if relaxed=True, it will be clipped and a warning issued instead. Defaults to None, for no maximum.
A string containing all the characters which are allowed in the salt string. An ValueError will be throw if any other characters are encountered. May be set to None to skip this check (but see in default_salt_chars).
[required] This attribute controls the set of characters use to generate new salt strings. By default, it mirrors salt_chars. If salt_chars is None, this attribute must be specified in order to generate new salts. Aside from that purpose, the main use of this attribute is for hashes which wish to generate salts from a restricted subset of salt_chars; such as accepting all characters, but only using a-z.
helper to normalize & validate user-provided salt string
normalized or generated salt
helper method for _norm_salt(); generates a new random salt string.
|Parameters:||salt_size – salt size to generate|
mixin for validating rounds parameter
|Parameters:||rounds – optional number of rounds hash should use|
In order for _norm_rounds() to do it’s job, the following attributes must be provided by the handler subclass:
The minimum number of rounds allowed. A ValueError will be thrown if the rounds value is too small. Defaults to 0.
The maximum number of rounds allowed. A ValueError will be thrown if the rounds value is larger than this. Defaults to None which indicates no limit to the rounds value.
If no rounds value is provided to constructor, this value will be used. If this is not specified, a rounds value must be specified by the application.
[required] The rounds parameter typically encodes a cpu-time cost for calculating a hash. This should be set to "linear" (the default) or "log2", depending on how the rounds value relates to the actual amount of time that will be required.
helper routine for normalizing rounds
rounds – None, or integer cost parameter.
normalized rounds value
mixin for hashes which use multiple prefix identifiers
For the hashes which may use multiple identifier prefixes, this mixin adds an ident keyword to constructor. Any value provided is passed through the norm_idents() method, which takes care of validating the identifier, as well as allowing aliases for easier specification of the identifiers by the user.
document this class’s usage
GenericHandler mixin which provides selecting from multiple backends.
finish documenting this class’s usage
For hashes which need to select from multiple backends, depending on the host environment, this class offers a way to specify alternate _calc_checksum() methods, and will dynamically chose the best one at runtime.
return name of currently active backend.
if no backend has been loaded, loads and returns name of default backend.
|if no backends are available.|
|Returns:||name of active backend|
load specified backend to be used for future _calc_checksum() calls
this method replaces _calc_checksum() with a method which uses the specified backend.
name of backend to load, defaults to "any". this can be any of the following values:
|Returns:||The return value of this function should be ignored.|
check if support is currently available for specified backend.
|Parameters:||name – name of backend to check for. defaults to "any", but can be any string accepted by set_backend().|
|if backend name is unknown|
|Returns:||True if backend is currently supported, else False.|
The following attributes and methods should be filled in by the subclass which is using HasManyBackends as a mixin:
This attribute should be a tuple containing the names of the backends which are supported. Two common names are "os_crypt" (if backend uses crypt), and "builtin" (if the backend is a pure-python fallback).
private class attribute checked by has_backend() to see if a specific backend is available, it should be either True or False. One of these should be provided by the subclass for each backend listed in backends.
mixin for classes which use decoded salt parameter
A variant of HasSalt which takes in decoded bytes instead of an encoded string.
document this class’s usage
mixin for classes which work with decoded checksum bytes
document this class’s usage
Show some walk-through examples of how to use GenericHandler and it’s mixins
GenericHandler mixin for classes which have no settings.
This mixin assumes the entirety of the hash ise stored in the checksum attribute; that the hash has no rounds, salt, etc. This class provides the following:
All that is required by subclasses is an implementation of the _calc_checksum() method.
Show some examples of how to use StaticHandler
wraps another handler, adding a constant prefix.
instances of this class wrap another password hash handler, altering the constant prefix that’s prepended to the wrapped handlers’ hashes.
myhandler = PrefixWrapper("myhandler", "md5_crypt", prefix="$mh$", orig_prefix="$1$")
Within it’s unittests, Passlib provides the HandlerCase class, which can be subclassed to provide a unittest-compatible test class capable of checking if a handler adheres to the Password Hash Interface.
As an example of how to use HandlerCase, the following is an annotated version of the unittest for passlib.hash.des_crypt:
from passlib.hash import des_crypt from passlib.tests.utils import HandlerCase # create a subclass for the handler... class DesCryptTest(HandlerCase): "test des-crypt algorithm" # [required] - store the handler object itself in the handler attribute handler = des_crypt # [optional] - if your hash only uses the first X characters of the password, # set that value here. otherwise leave the default (-1). secret_size = 8 # [required] - this should be a list of (password, hash) pairs, # which should all verify correctly using your handler. # it is recommend include pairs which test all of the following: # # * empty string & short strings for passwords # * passwords with 2 byte unicode characters # * hashes with varying salts, rounds, and other options known_correct_hashes = ( # format: (password, hash) ('', 'OgAwTx2l6NADI'), (' ', '/Hk.VPuwQTXbc'), ('test', 'N1tQbOFcM5fpg'), ('Compl3X AlphaNu3meric', 'um.Wguz3eVCx2'), ('4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#', 'sNYqfOyauIyic'), ('AlOtBsOl', 'cEpWz5IUCShqM'), (u'hell\u00D6', 'saykDgk3BPZ9E'), ) # [optional] - if there are hashes which are similar in format # to your handler, and you want to make sure :meth:`identify` # does not return ``True`` for such hashes, # list them here. otherwise this can be omitted. # known_unidentified_hashes = [ # bad char in otherwise correctly formatted hash '!gAwTx2l6NADI', ]
base class for testing password hash handlers (esp passlib.utils.handlers subclasses)
In order to use this to test a handler, create a subclass will all the appropriate attributes filled as listed in the example below, and run the subclass via unittest.
Document all of the options HandlerCase offers.
This is subclass of unittest.TestCase (or unittest2.TestCase if available).