a fWc X@sdZddlZeeZddlmZmZmZm Z ddl m Z m Z ddl mZmZmZmZddlmZmZddlmmZdgZGdddejejejejZdS) z:passlib.handlers.scram - hash for SCRAM credential storageN)consteqsaslprep to_native_str splitcomma) ab64_decode ab64_encode) bascii_to_str iteritemsunative_string_types) pbkdf2_hmacnorm_hash_namescramcseZdZdZdZdZedZdZdZ dZ dZ dZ d Z gd Zgd Zd Zed dZed'ddZeddZeddZddZed(fdd Zd)fdd Zd*ddZedd Zfd!d"Zd+d#d$Zed,d%d&ZZS)-raZThis class provides a format for storing SCRAM passwords, and follows the :ref:`password-hash-api`. It supports a variable-length salt, and a variable number of rounds. The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: :type salt: bytes :param salt: Optional salt bytes. If specified, the length must be between 0-1024 bytes. If not specified, a 12 byte salt will be autogenerated (this is recommended). :type salt_size: int :param salt_size: Optional number of bytes to use when autogenerating new salts. Defaults to 12 bytes, but can be any value between 0 and 1024. :type rounds: int :param rounds: Optional number of rounds to use. Defaults to 100000, but must be within ``range(1,1<<32)``. :type algs: list of strings :param algs: Specify list of digest algorithms to use. By default each scram hash will contain digests for SHA-1, SHA-256, and SHA-512. This can be overridden by specify either be a list such as ``["sha-1", "sha-256"]``, or a comma-separated string such as ``"sha-1, sha-256"``. Names are case insensitive, and may use :mod:`!hashlib` or `IANA `_ hash names. :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 In addition to the standard :ref:`password-hash-api` methods, this class also provides the following methods for manipulating Passlib scram hashes in ways useful for pluging into a SCRAM protocol stack: .. automethod:: extract_digest_info .. automethod:: extract_digest_algs .. automethod:: derive_digest )saltZ salt_sizeroundsalgs$scram$ iilZlinear)sha-1sha-256sha-512)rrzsha-224zsha-384rNcCs8t|d}||}|j}|s&td|j|j||fS)areturn (salt, rounds, digest) for specific hash algorithm. :type hash: str :arg hash: :class:`!scram` hash stored for desired user :type alg: str :arg alg: Name of digest algorithm (e.g. ``"sha-1"``) requested by client. This value is run through :func:`~passlib.crypto.digest.norm_hash_name`, so it is case-insensitive, and can be the raw SCRAM mechanism name (e.g. ``"SCRAM-SHA-1"``), the IANA name, or the hashlib name. :raises KeyError: If the hash does not contain an entry for the requested digest algorithm. :returns: A tuple containing ``(salt, rounds, digest)``, where *digest* matches the raw bytes returned by SCRAM's :func:`Hi` function for the stored password, the provided *salt*, and the iteration count (*rounds*). *salt* and *digest* are both raw (unencoded) bytes. ianazscram hash contains no digests)r from_stringchecksum ValueErrorrr)clshashalgselfchkmapr!:/usr/lib/python3.9/site-packages/passlib/handlers/scram.pyextract_digest_info|s   zscram.extract_digest_inforcs.||j}dkr|Sfdd|DSdS)aReturn names of all algorithms stored in a given hash. :type hash: str :arg hash: The :class:`!scram` hash to parse :type format: str :param format: This changes the naming convention used by the returned algorithm names. By default the names are IANA-compatible; possible values are ``"iana"`` or ``"hashlib"``. :returns: Returns a list of digest algorithms; e.g. ``["sha-1"]`` rcsg|]}t|qSr!r .0rformatr!r" z-scram.extract_digest_algs..N)rr)rrr(rr!r'r"extract_digest_algss zscram.extract_digest_algscCs&t|tr|d}t|t|||S)a;helper to create SaltedPassword digest for SCRAM. This performs the step in the SCRAM protocol described as:: SaltedPassword := Hi(Normalize(password), salt, i) :type password: unicode or utf-8 bytes :arg password: password to run through digest :type salt: bytes :arg salt: raw salt data :type rounds: int :arg rounds: number of iterations. :type alg: str :arg alg: name of digest to use (e.g. ``"sha-1"``). :returns: raw bytes of ``SaltedPassword`` zutf-8) isinstancebytesdecoder r)rpasswordrrrr!r!r" derive_digests  zscram.derive_digestc Cs<t|dd}|ds"tj||ddd}t|dkrLtj||\}}}t|}|t |krvtj|zt | d}Wnt ytj|Yn0|stj|nrd|vr$d}i} |dD]J} | d\} } zt | d| | <Wqt ytj|Yq0qn|}d} |||| |d S) Nasciirr$=,)rrrr) r startswithuhexcZInvalidHashErrorsplitlenZMalformedHashErrorintstrrencode TypeError) rrpartsZ rounds_strZsalt_strchk_strrrrr Zpairrdigestr!r!r"rsB          zscram.from_stringcs>tt|j}|jdfdd|jD}d|j||fS)Nr6c3s&|]}d|tt|fVqdS)z%s=%sN)rrr%r r!r" sz"scram.to_string..z$scram$%d$%s$%s)rrrrjoinrr)rrrAr!rCr" to_string s zscram.to_stringc sF|dur|dusJ|}tt|jfi|}|durB|||_|SN)superrusing _norm_algs default_algs)rrKrkwdssubcls __class__r!r"rIs  z scram.usingc stt|jfi||j}|dur@|dur4td||}nN|durX||}n6|jrt|j }|||ksJd|fnt d||_ dS)Nz+checksum & algs kwds are mutually exclusivezinvalid default algs: %rzno algs list specified) rHr__init__r RuntimeErrorrJkeysZ use_defaultslistrKr?r)rrrLZ digest_maprNr!r"rP+s  zscram.__init__FcCst|tstj|ddt|D]X\}}|t|dkrFtd|ft|dkr`td|ft|t s"tj|ddq"d |vrtd |S) Ndictrrz*malformed algorithm name in scram hash: %r z0SCRAM limits algorithm names to 9 characters: %rz raw bytesZdigestsr-sha-1 must be in algorithm list of scram hash) r,rTr8r9ZExpectedTypeErrorr r rr;r-)rrZrelaxedrrBr!r!r"_norm_checksum>s    zscram._norm_checksumcCsRt|trt|}tdd|D}tdd|Dr>tdd|vrNtd|S)znormalize algs parametercss|]}t|dVqdS)rNr$r%r!r!r"rDUr*z#scram._norm_algs..css|]}t|dkVqdS)rUN)r;r%r!r!r"rDVr*z-SCRAM limits alg names to max of 9 charactersrrV)r,r rsortedanyr)rrr!r!r"rJPs zscram._norm_algsc s,t|j|jsdStt|jfi|S)NT)setr issupersetrKrHr_calc_needs_update)rrLrNr!r"r\`szscram._calc_needs_updatecsF|j|j|j|r$|Stfdd|jDSdS)Nc3s |]}||fVqdSrGr!r%rrrsecretr!r"rDvsz'scram._calc_checksum..)rrr0rTr)rr^rr!r]r"_calc_checksummszscram._calc_checksumc Cst|||}|j}|s2td|j|jf|rd}}t|D]R\}} |||} t| t| krtd|t| t| ft | | rd}qFd}qF|r|rtdq|Sn:|j D]*}||vr|||} t | ||Sqt ddS)Nz.expected %s hash, got %s config string insteadFz+mis-sized %s digest in scram hash: %r != %rTz4scram hash verified inconsistently, may be corruptedzsha-1 digest not found!) r8Zvalidate_secretrrrnamer r_r;r _verify_algsAssertionError) rr^rZfullrr ZcorrectZfailedrrBotherr!r!r"verify{s4        z scram.verify)r)NN)N)F)N)F)__name__ __module__ __qualname____doc__r`Z setting_kwdsr identZdefault_salt_sizeZ max_salt_sizeZdefault_roundsZ min_roundsZ max_roundsZ rounds_costrKrar classmethodr#r+r0rrFrIrPrWrJr\r_rd __classcell__r!r!rNr"rs@A  %   /    )rhZloggingZ getLoggerrelogZ passlib.utilsrrrrZpasslib.utils.binaryrrZpasslib.utils.compatrr r r Zpasslib.crypto.digestr r Zpasslib.utils.handlersZutilshandlersr8__all__Z HasRoundsZ HasRawSaltZHasRawChecksumZGenericHandlerrr!r!r!r"s