diff options
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | docs/lib/passlib.context-options.rst | 36 | ||||
-rw-r--r-- | passlib/apps.py | 5 | ||||
-rw-r--r-- | passlib/context.py | 62 | ||||
-rw-r--r-- | passlib/tests/test_context.py | 58 |
5 files changed, 2 insertions, 164 deletions
@@ -68,11 +68,6 @@ Release History .. currentmodule:: passlib.context - * :class:`~CryptContext` now supports a :ref:`passprep <passprep>` option, - which can be used to run all passwords through SASLPrep (:rfc:`4013`), - in order to normalize their unicode representation before hashing - [issue 24]. - * The :class:`!CryptContext` option :ref:`min_verify_time <min-verify-time>` has been deprecated, will be ignored in release 1.7, and will be removed in release 1.8. diff --git a/docs/lib/passlib.context-options.rst b/docs/lib/passlib.context-options.rst index ece9033..52146a4 100644 --- a/docs/lib/passlib.context-options.rst +++ b/docs/lib/passlib.context-options.rst @@ -138,42 +138,6 @@ and :samp:`{option}` is the name of the specific options being set. These are configurable per-context limits, they will be clipped by any hard limits set in the hash algorithm itself. -.. _passprep: - -:samp:`{hash}__passprep` - - Normalize unicode passwords before passing them to the underlying - hash algorithm. This is primarily useful if users are likely - to use non-ascii characters in their password (e.g. vowels characters - with accent marks), which unicode offers multiple representations for. - - This may be one of the following values: - - * ``"raw"`` - use all unicode inputs as-is (the default). - unnormalized unicode input may not verify against a hash - generated from normalized unicode input (or vice versa). - - * ``"saslprep"`` - run all passwords through the SASLPrep - unicode normalization algorithm (:rfc:`4013`) before hashing. - this is recommended for new deployments, particularly - in non-ascii environments. - - * ``"saslprep,raw"`` - compatibility mode: encryption of new passwords - will be run through SASLPrep; but verification will be done - against the SASLPrep *and* raw versions of the password. This allows - existing hashes that were generated from unnormalized input - to continue to work. - - .. note:: - - It is recommended to set this for all hashes via ``all__passprep``, - instead of settings it per algorithm. - - .. note:: - - Due to a missing :mod:`!stringprep` module, this feature - is not available on Jython. - :samp:`{hash}__{setting}` Any other option values, which match the name of a parameter listed diff --git a/passlib/apps.py b/passlib/apps.py index 017de7e..9bb4a64 100644 --- a/passlib/apps.py +++ b/passlib/apps.py @@ -29,11 +29,6 @@ custom_app_context = LazyCryptContext( #choose some reasonbly strong schemes schemes=["sha512_crypt", "sha256_crypt"], - # TODO: enable passprep for default policy? would definitely be a good - # idea for most applications; but want passprep to get a release or - # two worth of deployment & feedback before turning it on here. - ## all__passprep = "saslprep,raw", - #set some useful global options all__vary_rounds = "10%", default="sha256_crypt" if sys_bits < 64 else "sha512_crypt", diff --git a/passlib/context.py b/passlib/context.py index 3d14238..57b1646 100644 --- a/passlib/context.py +++ b/passlib/context.py @@ -18,7 +18,7 @@ from warnings import warn #libs from passlib.exc import PasslibConfigWarning, ExpectedStringError, ExpectedTypeError from passlib.registry import get_crypt_handler, _validate_handler_name -from passlib.utils import is_crypt_handler, rng, saslprep, tick, to_bytes, \ +from passlib.utils import is_crypt_handler, rng, tick, to_bytes, \ to_unicode from passlib.utils.compat import bytes, iteritems, num_types, \ PY3, PY_MIN_32, unicode, SafeConfigParser, \ @@ -62,12 +62,6 @@ _coerce_scheme_options = dict( salt_size=int, ) -# dict mapping passprep policy name -> implementation -_passprep_funcs = dict( - saslprep=saslprep, - raw=lambda s: s, -) - def _splitcomma(source): "split comma-separated string into list of strings" source = source.strip() @@ -643,7 +637,7 @@ class _CryptRecord(object): #================================================================ def __init__(self, handler, category=None, deprecated=False, min_rounds=None, max_rounds=None, default_rounds=None, - vary_rounds=None, min_verify_time=None, passprep=None, + vary_rounds=None, min_verify_time=None, **settings): # store basic bits self.handler = handler @@ -664,9 +658,6 @@ class _CryptRecord(object): self.identify = handler.identify self.genhash = handler.genhash - # let stringprep code wrap genhash/encrypt/verify if needed - self._init_passprep(passprep) - #================================================================ # virtual attrs #================================================================ @@ -991,55 +982,6 @@ class _CryptRecord(object): return False #================================================================ - # password stringprep - #================================================================ - def _init_passprep(self, value): - # NOTE: all of this code assumes secret uses utf-8 encoding if bytes. - if not value: - return - self._stringprep = value - names = _splitcomma(value) - if names == ["raw"]: - return - funcs = [_passprep_funcs[name] for name in names] - - first = funcs[0] - def wrap(orig): - def wrapper(secret, *args, **kwds): - if isinstance(secret, bytes): - secret = secret.decode("utf-8") - return orig(first(secret), *args, **kwds) - update_wrapper(wrapper, orig) - wrapper._wrapped = orig - return wrapper - - # wrap genhash & encrypt so secret is prep'd - self.genhash = wrap(self.genhash) - self.encrypt = wrap(self.encrypt) - - # wrap verify so secret is prep'd - if len(funcs) == 1: - self.verify = wrap(self.verify) - else: - # if multiple fallback prep functions, - # try to verify with each of them. - verify = self.verify - def wrapper(secret, *args, **kwds): - if isinstance(secret, bytes): - secret = secret.decode("utf-8") - seen = set() - for prep in funcs: - tmp = prep(secret) - if tmp not in seen: - if verify(tmp, *args, **kwds): - return True - seen.add(tmp) - return False - update_wrapper(wrapper, verify) - wrapper._wrapped = verify - self.verify = wrapper - - #================================================================ # eoc #================================================================ diff --git a/passlib/tests/test_context.py b/passlib/tests/test_context.py index 2abbccf..20b6dc6 100644 --- a/passlib/tests/test_context.py +++ b/passlib/tests/test_context.py @@ -1266,64 +1266,6 @@ sha512_crypt__min_rounds = 45000 self.assertAlmostEqual(elapsed, max_delay, delta=delta) self.consumeWarningList(wlog, ".*verify exceeded min_verify_time") - def test_61_passprep(self): - "test passprep option" - self.require_stringprep() - - # saslprep should normalize pu -> pn - pu = u("a\u0300") # unnormalized unicode - pn = u("\u00E0") # normalized unicode - - # create contexts w/ various options - craw = CryptContext(["md5_crypt"]) - cnorm = CryptContext(["md5_crypt"], all__passprep="saslprep") - cback = CryptContext(["md5_crypt"], all__passprep="saslprep,raw") - clst = [craw,cnorm,cback] - - # check raw encrypt against verify methods - h = craw.encrypt(pu) - - self.assertTrue(craw.verify(pu, h)) - self.assertFalse(cnorm.verify(pu, h)) - self.assertTrue(cback.verify(pu, h)) - - self.assertFalse(craw.verify(pn, h)) - self.assertFalse(craw.verify(pn, h)) - self.assertFalse(craw.verify(pn, h)) - - # check normalized encrypt against verify methods - for ctx in [cnorm, cback]: - h = ctx.encrypt(pu) - - self.assertFalse(craw.verify(pu, h)) - self.assertTrue(cnorm.verify(pu, h)) - self.assertTrue(cback.verify(pu, h)) - - for ctx2 in clst: - self.assertTrue(ctx2.verify(pn, h)) - - # check all encrypts leave normalized input alone - for ctx in clst: - h = ctx.encrypt(pn) - - self.assertFalse(craw.verify(pu, h)) - self.assertTrue(cnorm.verify(pu, h)) - self.assertTrue(cback.verify(pu, h)) - - for ctx2 in clst: - self.assertTrue(ctx2.verify(pn, h)) - - # test invalid name - self.assertRaises(KeyError, CryptContext, ["md5_crypt"], - all__passprep="xxx") - - # test per-hash passprep - ctx = CryptContext(["md5_crypt", "sha256_crypt"], - all__passprep="raw", sha256_crypt__passprep="saslprep", - ) - self.assertFalse(ctx.verify(pu, ctx.encrypt(pn, scheme="md5_crypt"))) - self.assertTrue(ctx.verify(pu, ctx.encrypt(pn, scheme="sha256_crypt"))) - #========================================================= # handler deprecation detectors #========================================================= |