nXc@sdZddlmZmZddlmZddlmZddlZddl Z ddl Z e j e Z ddlmZeaeaeaeaddlmZmZmZddlmZmZmZmZmZmZm Z m!Z!dd l"m#Z#dd l$m%Z%m&Z&m'Z'm(Z(ddl)j*j+Z,d gZ-e%d Z.e%d Z/e%dZ0e%dZ1e%dZ2dZ3dZ4dZ5de,j6e,j7e,j8e,j9e,j:e,j;fdYZ<de<fdYZ=de<fdYZ>de<fdYZ?de<fdYZ@de<fdYZAd e<fd!YZBd e=e<fd"YZCe%d#ZDd$eCfd%YZEd&eEfd'YZFdS((spasslib.bcrypt -- implementation of OpenBSD's BCrypt algorithm. TODO: * support 2x and altered-2a hashes? http://www.openwall.com/lists/oss-security/2011/06/27/9 * deal with lack of PY3-compatibile c-ext implementation i(twith_statementtabsolute_import(t b64encode(tsha256N(twarn(tPasslibHashWarningtPasslibSecurityWarningtPasslibSecurityError(t safe_cryptt repeat_stringtto_bytest parse_versiontrngt getrandstrt test_cryptt to_unicode(tbcrypt64(tut uascii_to_strtunicodet str_to_uasciitbcrypts$2$s$2a$s$2x$s$2y$s$2b$ts<$2a$04$5BJqKfqMQvV7nS.yUguNcueVirQqDBGaLXSqj.rs.pZPlNR0UX/HKcCsRyddl}Wntk r$dSXyddlm}Wntk rMtSXtS(s! internal helper which tries to distinguish pybcrypt vs bcrypt. :returns: True if cext-based py-bcrypt, False if ffi-based bcrypt, None if 'bcrypt' module not found. .. versionchanged:: 1.6.3 Now assuming bcrypt installed, unless py-bcrypt explicitly detected. Previous releases assumed py-bcrypt by default. Making this change since py-bcrypt is (apparently) unmaintained and static, whereas bcrypt is being actively maintained, and it's internal structure may shift. iN(t __version__(Rt ImportErrortNonetbcrypt._bcryptRtFalsetTrue(RR((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyt_detect_pybcrypt4s  t _BcryptCommoncBsleZdZdZdZdZejZe Z e e e ee fZie ed6e ed6eed 6e ed 6Zd ZZejZd Zd ZdZdZdZeZeZeZeZeZe Z e!dZ"dZ#dZ$e!dZ%e!dZ&e!dZ'e!dZ(edZ)dZ*e!dZ+dZ,e!edZ-RS(s& Base class which implements brunt of BCrypt code. This is then subclassed by the various backends, to override w/ backend-specific methods. When a backend is loaded, the bases of the 'bcrypt' class proper are modified to prepend the correct backend-specific subclass. Rtsalttroundstidentttruncate_errorit2t2at2yt2bii itlog2iHc Cs|j|\}}|tkr0tdn|jtd\}}t|}|td|fkrtjj|dn|d |d}}|d|d|d|pdd |S( Ns>crypt_blowfish's buggy '2x' hashes are not currently supportedt$s%02dsmalformed cost fieldiR RtchecksumR!( t _parse_identtIDENT_2Xt ValueErrortsplitRtinttuhtexctMalformedHashErrorR( tclsthashR!ttailt rounds_strtdataR Rtchk((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyt from_strings   cCs2td|j|j|j|jf}t|S(Ns %s%02d$%s%s(RR!R RR)R(tselfR3((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyt to_strings(cCs)td||j|jf}t|S(s5internal helper to prepare config string for backendss %s%02d$%s(RR RR(R9R!tconfig((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyt _get_configscKsdt|tr!|jd}n|jtrK|dtjdkrKtStt |j ||S(Ntasciiii( t isinstancetbytestdecodet startswithtIDENT_2ARt _padinfo2RtsuperRt needs_update(R2R3tkwds((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyREs &cCs*|j|r"|j|jS|SdS(s<helper to normalize hash, correcting any bcrypt padding bitsN(tidentifyR8R:(R2R3((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pytnormhashscCs"tt|j}tj|S(N(RDRt_generate_saltRt repair_unused(R2R((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRIscKsJtt|j||}tj|\}}|rFtdtn|S(Nsencountered a bcrypt salt with incorrectly set padding bits; you may want to use bcrypt.normhash() to fix this; this will be an error under Passlib 2.0(RDRt _norm_saltRtcheck_repair_unusedRR(R2RRFtchanged((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRKs cCsMtt|j|d|}tj|\}}|rItdtn|S(Ntrelaxedsencountered a bcrypt hash with incorrectly set padding bits; you may want to use bcrypt.normhash() to fix this; this will be an error under Passlib 2.0(RDRt_norm_checksumRRLRR(R9R)RNRM((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyROs s9 -- recommend you install one (e.g. 'pip install bcrypt')c s^|jr tS|jtftr;tjjf7nfd}fd}fdfd}d}|d|}|stdn(|tkrt|_ t j dn|dt }|std nX|tkr#td n9|t t r\td tjjt|_nt jd d }|d|}|stdn<|tkrt|_t j dn|t|tt jd d} |d| }|s tdnE|tkr4t|_t j dnt|_|t|tt|_tS(s helper called by from backend mixin classes' _load_backend_mixin() -- invoked after backend imports have been loaded, and performs feature detection & testing common to all backends. csYy||SWnAk r%tStk rT}tjd||dttSXdS(s8verify() wrapper which traps 'unknown identifier' errorss<trapped unexpected response from %r backend: verify(%r, %r):texc_infoN(tNotImplementedtAssertionErrortlogtdebugR(tsecretR3terr(tbackendt err_typestverify(s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyt safe_verify"s  cszd}|jdd}||r;tdn|jdd}||svtd|fndS(s helper to check for cryptblowfish 8bit bug (fixed in 2y/2b); even though it's not known to be present in any of passlib's backends. this is treated as FATAL, because it can easily result in seriously malformed hashes, and we can't correct for it ourselves. test cases from reference hash is the incorrectly generated $2x$ hash taken from above url sR=s805$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3espasslib.hash.bcrypt: Your installation of the %r backend is vulnerable to the crypt_blowfish 8-bit bug (CVE-2011-2483), and should be upgraded or replaced with another backend.s805$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCqs(%s backend failed to verify %s 8bit hashN(tencodeRt RuntimeError(R!RUtbug_hasht correct_hash(RWRY(s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pytassert_lacks_8bit_bug4s  cssddd }|jdd}||r4tS|jdd}||sotd|fntS(s> check for bsd wraparound bug (fixed in 2b) this is treated as a warning, because it's rare in the field, and pybcrypt (as of 2015-7-21) is unpatched, but some people may be stuck with it. test cases from NOTE: reference hash is of password "0"*72 NOTE: if in future we need to deliberately create hashes which have this bug, can use something like 'hashpw(repeat_string(secret[:((1+secret) % 256) or 1]), 72)' t 0123456789iiR=s804$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6s804$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Ois.%s backend failed to verify %s wraparound hash(R[RR\R(R!RUR]R^(RWRY(s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pytdetect_wrap_bugOscs*|sdStd|fdS(Ns1%s backend unexpectedly has wraparound bug for %s(R\(R!(RWRa(s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pytassert_lacks_wrap_bugjs s;$2$04$5BJqKfqMQvV7nS.yUguNcuRfMMOXK0xPWavM7pOzjEi5ze5T1k8/Sttests %s incorrectly rejected $2$ hashs1%r backend lacks $2$ support, enabling workarounds!%s incorrectly rejected $2a$ hashs %s lacks support for $2a$ hashesspasslib.hash.bcrypt: Your installation of the %r backend is vulnerable to the bsd wraparound bug, and should be upgraded or replaced with another backend (enabling workaround for now).R$R%s!%s incorrectly rejected $2y$ hashs2%r backend lacks $2y$ support, enabling workaroundR&s!%s incorrectly rejected $2b$ hashs2%r backend lacks $2b$ support, enabling workaround(t_workrounds_initializedRRYR,t _bcryptortenginet SaltErrorR\RQt_lacks_20_supportRSRTt TEST_HASH_2ARBRR/R0Rt_has_2a_wraparound_bugtreplacet_lacks_2y_supporttIDENT_2Yt_lacks_2b_supporttIDENT_2Bt_fallback_ident( t mixin_clsRWtdryrunRZR_Rbt test_hash_20tresultt test_hash_2yt test_hash_2b((RWRaRXRYs;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyt_finalize_backend_mixin sb                     cCs|j||jd|jS(s common helper for backends to implement _calc_checksum(). takes in secret, returns (secret, ident) pair, tnew(t_norm_digest_argsR!t use_defaults(R9RU((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyt_prepare_digest_argsscCsUt|tr!|jd}ntj||rD|j|nt|kretjj|n|j rt |dkr|d }n|t krn|t kr|j rK|j}qKn|tkr|jrK|j}qKng|tkr |jrK|rt|d}n|j}qKn+|tkr;tdntd|||fS(Nsutf-8iiHs.$2x$ hashes not currently supported by passlibsunexpected ident value: %r(R>RR[R/tvalidate_secrett_check_truncate_policyt_BNULLR0tNullPasswordErrorRjtlenRBRoRnRpRmRltIDENT_2RhR R+R\RR(R2RUR!Rx((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRys4           (ssaltsroundssidentR"(.t__name__t __module__t__doc__tnamet setting_kwdst checksum_sizeRtcharmaptchecksum_charsRot default_identRRBR+Rmt ident_valuesRt ident_aliasest min_salt_sizet max_salt_sizet salt_charstdefault_roundst min_roundst max_roundst rounds_costt truncate_sizeRRdRjRhRlRnRpt classmethodR8R:R<RERHRIRKROt_no_backend_suggestionRwR{Ry(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRYsF  *      t _NoBackendcBseZdZdZRS(s mixin used before any backend has been loaded. contains stubs that force loading of one of the available backends. cCs |jtt|j|S(N(t_stub_requires_backendRDRt_calc_checksum(R9RU((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRs (RRRR(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyR st_BcryptBackendcBs&eZdZedZdZRS(s- backend which uses 'bcrypt' package cCstr tSyddlaWntk r1tSXytjj}Wn tjddt d}nXtj d||j ||S(Nis&(trapped) error reading bcrypt versionRPs s%detected 'bcrypt' backend, version %r( RRRt_bcryptRt __about__RRStwarningRRTRw(RqRRrtversion((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyt_load_backend_mixin#s   cCsh|j|\}}|j|}t|trE|jd}ntj||}|djdS(NR=i(R{R<R>RR[RthashpwR@(R9RUR!R;R3((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRIs (RRRRRR(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRs&t_BcryptorBackendcBs&eZdZedZdZRS(s/ backend which uses 'bcryptor' package cCs5yddlaWntk r$tSX|j||S(Ni(tbcryptorReRRRw(RqRRr((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyR`s  cCsP|j|\}}|j|}tjjtj||}t|dS(Ni(R{R<ReRftEngineRthash_keyR(R9RUR!R;R3((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRjs(RRRRRR(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyR[s t_PyBcryptBackendcBs;eZdZdZedZdZdZeZ RS(s/ backend which uses 'pybcrypt' package cCsts tSyddlaWntk r1tSXytjj}Wn tjddt d}nXtj d|t |pd}|d krt d|t jj|jdkrddl}|j|_n|jj|_n|j||S( Nis((trapped) error reading pybcrypt versionRPs s'detected 'pybcrypt' backend, version %risapy-bcrypt %s has a major security vulnerability, you should upgrade to py-bcrypt 0.3 immediately.(ii(ii(RRRt _pybcryptRRRRSRRRTR RR/R0Rt _calc_lockRt threadingtLockt_calc_checksum_threadsafet__func__RRw(RqRRrRtvinfoR((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRs*     cCs!|j|j|SWdQXdS(N(Rt_calc_checksum_raw(R9RU((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRs cCsD|j|\}}|j|}tj||}t|dS(Ni(R{R<RRR(R9RUR!R;R3((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRsN( RRRRRRRRRR(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRxs !  t_OsCryptBackendcBs&eZdZedZdZRS(s0 backend which uses :func:`crypt.crypt` cCs#tdtstS|j||S(NRc(RRiRRw(RqRRr((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRscCsW|j|\}}|j|}t||}|rA|dStjjddS(Nissnon-utf8 encoded passwords can't be handled by crypt.crypt() under python3, recommend running `pip install bcrypt`.(R{R<RR/R0tMissingBackendError(R9RUR!R;R3((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRs (RRRRRR(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRst_BuiltinBackendcBs&eZdZedZdZRS(sA backend which uses passlib's pure-python implementation cCsYddlm}|tjjds9tjdtSddlm a |j ||S(Ni(tas_booltPASSLIB_BUILTIN_BCRYPTs@bcrypt 'builtin' backend not enabled via $PASSLIB_BUILTIN_BCRYPT(t raw_bcrypt( t passlib.utilsRtostenvirontgetRSRTRtpasslib.crypto._blowfishRt_builtin_bcryptRw(RqRRrR((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRs  cCsM|j|\}}t||dd!|jjd|j}|jdS(NiiR=(R{RRR[R R@(R9RUR!R7((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRs(RRRRRR(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRs cBsJeZdZdZeZied6ed6e d6e d6e d6e d6Z RS(s This class implements the BCrypt password hash, and follows the :ref:`password-hash-api`. It supports a fixed-length salt, and a variable number of rounds. The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: :type salt: str :param salt: Optional salt string. If not specified, one will be autogenerated (this is recommended). If specified, it must be 22 characters, drawn from the regexp range ``[./0-9A-Za-z]``. :type rounds: int :param rounds: Optional number of rounds to use. Defaults to 12, must be between 4 and 31, inclusive. This value is logarithmic, the actual number of iterations used will be :samp:`2**{rounds}` -- increasing the rounds by +1 will double the amount of time taken. :type ident: str :param ident: Specifies which version of the BCrypt algorithm will be used when creating a new hash. Typically this option is not needed, as the default (``"2b"``) is usually the correct choice. If specified, it must be one of the following: * ``"2"`` - the first revision of BCrypt, which suffers from a minor security flaw and is generally not used anymore. * ``"2a"`` - some implementations suffered from rare security flaws, replaced by 2b. * ``"2y"`` - format specific to the *crypt_blowfish* BCrypt implementation, identical to ``"2b"`` in all but name. * ``"2b"`` - latest revision of the official BCrypt algorithm, current default. :param bool truncate_error: By default, BCrypt will silently truncate passwords larger than 72 bytes. Setting ``truncate_error=True`` will cause :meth:`~passlib.ifc.PasswordHash.hash` to raise a :exc:`~passlib.exc.PasswordTruncateError` instead. .. versionadded:: 1.7 :type relaxed: bool :param relaxed: By default, providing an invalid value for one of the other keywords will result in a :exc:`ValueError`. If ``relaxed=True``, and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` will be issued instead. Correctable errors include ``rounds`` that are too small or too large, and ``salt`` strings that are too long. .. versionadded:: 1.6 .. versionchanged:: 1.6 This class now supports ``"2y"`` hashes, and recognizes (but does not support) the broken ``"2x"`` hashes. (see the :ref:`crypt_blowfish bug ` for details). .. versionchanged:: 1.6 Added a pure-python backend. .. versionchanged:: 1.6.3 Added support for ``"2b"`` variant. .. versionchanged:: 1.7 Now defaults to ``"2b"`` variant. RtpybcryptRtos_crypttbuiltin(sbcryptRsbcryptorRRN(RRRtbackendsRt_backend_mixin_targetRRRRRRRt_backend_mixin_map(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRsA R(t_wrapped_bcryptcBs<eZdZedejDZdZedZ RS(s abstracts out some bits bcrypt_sha256 & django_bcrypt_sha256 share. - bypass backend-loading wrappers for hash() etc - disable truncation support, sha256 wrappers don't need it. ccs!|]}|dkr|VqdS(R"N(struncate_error((t.0telem((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pys _scCsdS(N((R2RU((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyR}ssN( RRRttupleRRRRRR}(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRYst bcrypt_sha256cBseZdZdZeefZdeZeZe dZ e j de j ZedZedZe dZdZd ZRS( sThis class implements a composition of BCrypt+SHA256, and follows the :ref:`password-hash-api`. It supports a fixed-length salt, and a variable number of rounds. The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept all the same optional keywords as the base :class:`bcrypt` hash. .. versionadded:: 1.6.2 .. versionchanged:: 1.7 Now defaults to ``"2b"`` variant. Rcs#tfdtjjDS(Nc3s%|]}|dkr|VqdS(iN((Rtitem(R(s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pys s(tdictRRtitems(R((Rs;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pytss$bcrypt-sha256$s ^ [$]bcrypt-sha256 [$](?P2[ab]) ,(?P\d{1,2}) [$](?P[^$]{22}) (?:[$](?P.{31}))? $ cCs)tj|}|stS|j|jS(N(R/tto_unicode_for_identifyRRAtprefix(R2R3((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRGsc Cst|dd}|j|js9tjj|n|jj|}|sftjj|n|j d}|jtj r|tj krtjj |n|d|j ddt |d|j dd|j dS( NR=R3R R!tvariantRR)tdigest( RRARR/R0tInvalidHashErrort_hash_retmatchR1tgroupt_UZEROtZeroPaddedRoundsErrorR.(R2R3tmR ((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyR8s! s$bcrypt-sha256$%s,%d$%s$%scCs8|j|jjt|j|j|jf}t|S(N(t _templateR!tstript_UDOLLARR RR)R(R9R3((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyR:scCsOt|tr!|jd}ntt|j}tt|j|S(Nsutf-8( R>RR[RRRRDRR(R9RUtkey((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyRs(RRRRRBRoRRRRRtretcompiletXRRRGR8RR:R(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyR|s      (GRt __future__RRtbase64RthashlibRRRtloggingt getLoggerRRStwarningsRRRRReRt passlib.excRRRRRR R R R R RRtpasslib.utils.binaryRtpasslib.utils.compatRRRRtpasslib.utils.handlerstutilsthandlersR/t__all__RRBR+RmRoR~RiRtSubclassBackendMixint TruncateMixint HasManyIdentst HasRoundstHasSalttGenericHandlerRRRRRRRRRRR(((s;/usr/lib/python2.7/site-packages/passlib/handlers/bcrypt.pyt sN   :"       %"=A#b #