a
    OʞhX                     @   s   d Z ddlZeeZddlmZmZmZm	Z	 ddl
mZmZ ddlmZmZmZmZ ddlmZmZ ddlm  mZ dgZG dd dejejejejZdS )	z:passlib.handlers.scram - hash for SCRAM credential storage    N)consteqsaslprepto_native_str
splitcomma)ab64_decodeab64_encode)bascii_to_str	iteritemsunative_string_types)pbkdf2_hmacnorm_hash_namescramc                       s   e Zd ZdZd ZdZedZdZdZ	dZ
dZdZd	Zg d
Zg dZdZedd Zed'ddZedd Zedd Zdd Zed( fdd	Zd) fdd	Zd*ddZedd  Z fd!d"Zd+d#d$Zed,d%d&Z  ZS )-r   aZ  This 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 <http://www.iana.org/assignments/hash-function-text-names>`_
        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_sizeroundsalgs$scram$   i   i    l    Zlinear)sha-1sha-256sha-512)r   r   zsha-224zsha-384r   Nc                 C   s8   t |d}| |}|j}|s&td|j|j|| fS )a  return (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.
        ianazscram hash contains no digests)r   from_stringchecksum
ValueErrorr   r   )clshashalgselfchkmap r!   Q/wd/webapps/venvs/v2025_4um/lib/python3.9/site-packages/passlib/handlers/scram.pyextract_digest_info|   s    

zscram.extract_digest_infor   c                    s.   |  |j} dkr|S  fdd|D S dS )a  Return 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"]``
        r   c                    s   g | ]}t | qS r!   r   .0r   formatr!   r"   
<listcomp>       z-scram.extract_digest_algs.<locals>.<listcomp>N)r   r   )r   r   r(   r   r!   r'   r"   extract_digest_algs   s    zscram.extract_digest_algsc                 C   s&   t |tr|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``
        zutf-8)
isinstancebytesdecoder   r   )r   passwordr   r   r   r!   r!   r"   derive_digest   s    

zscram.derive_digestc              	   C   s<  t |dd}|ds"tj| |dd  d}t|dkrLtj| |\}}}t|}|t	|krvtj| zt
|d}W n ty   tj| Y n0 |stj| nrd|v r$d }i }	|dD ]J}
|
d\}}zt
|d|	|< W q ty   tj| Y q0 qn|}d }	| |||	|d	S )
Nasciir   r      $   =,)r   r   r   r   )r   
startswithuhexcZInvalidHashErrorsplitlenZMalformedHashErrorintstrr   encode	TypeError)r   r   partsZ
rounds_strZsalt_strchk_strr   r   r   r    pairr   digestr!   r!   r"   r      sB    


zscram.from_stringc                    s>   t t| j}| j d fdd| jD }d| j||f S )Nr6   c                 3   s&   | ]}d |t t | f V  qdS )z%s=%sN)r   r   r%   r    r!   r"   	<genexpr>  s   z"scram.to_string.<locals>.<genexpr>z$scram$%d$%s$%s)r   r   r   r   joinr   r   )r   r   rA   r!   rD   r"   	to_string  s    zscram.to_stringc                    sF   |d ur|d u sJ |}t t| jf i |}|d urB| ||_|S N)superr   using
_norm_algsdefault_algs)r   rL   r   kwdssubcls	__class__r!   r"   rJ     s    zscram.usingc                    s   t t| jf i | | j}|d ur@|d ur4td| |}nN|d urX| | }n6| jrt| j	}| ||ksJ d|f nt
d|| _d S )Nz+checksum & algs kwds are mutually exclusivezinvalid default algs: %rzno algs list specified)rI   r   __init__r   RuntimeErrorrK   keysZuse_defaultslistrL   r?   r   )r   r   rM   Z
digest_maprO   r!   r"   rQ   +  s    
zscram.__init__Fc                 C   s   t |tstj|ddt|D ]X\}}|t|dkrFtd|f t|dkr`td|f t |t	s"tj|ddq"d	|vrtd
|S )Ndictr   r   z*malformed algorithm name in scram hash: %r	   z0SCRAM limits algorithm names to 9 characters: %rz	raw bytesdigestsr   -sha-1 must be in algorithm list of scram hash)
r,   rU   r8   r9   ZExpectedTypeErrorr	   r   r   r;   r-   )r   r   relaxedr   rC   r!   r!   r"   _norm_checksum>  s     

zscram._norm_checksumc                 C   sR   t |trt|}tdd |D }tdd |D r>tdd|vrNtd|S )znormalize algs parameterc                 s   s   | ]}t |d V  qdS )r   Nr$   r%   r!   r!   r"   rE   U  r*   z#scram._norm_algs.<locals>.<genexpr>c                 s   s   | ]}t |d kV  qdS )rV   N)r;   r%   r!   r!   r"   rE   V  r*   z-SCRAM limits alg names to max of 9 charactersr   rX   )r,   r   r   sortedanyr   )r   r   r!   r!   r"   rK   P  s    
zscram._norm_algsc                    s,   t | j| jsdS tt| jf i |S )NT)setr   
issupersetrL   rI   r   _calc_needs_update)r   rM   rO   r!   r"   r_   `  s    zscram._calc_needs_updatec                    sF   | j | j| j |r$ |S t fdd| jD S d S )Nc                 3   s    | ]}| |fV  qd S rH   r!   r%   r   r   r   secretr!   r"   rE   v  s   z'scram._calc_checksum.<locals>.<genexpr>)r   r   r0   rU   r   )r   ra   r   r!   r`   r"   _calc_checksumm  s    zscram._calc_checksumc                 C   s   t | | |}|j}|s2td| j| jf |rd }}t|D ]R\}}	|||}
t|	t|
krtd|t|	t|
f t	|
|	rd}qFd}qF|r|rtdq|S n:|j
D ]*}||v r|||}
t	|
||   S qtdd S )Nz.expected %s hash, got %s config string insteadFz+mis-sized %s digest in scram hash: %r != %rTz4scram hash verified inconsistently, may be corruptedzsha-1 digest not found!)r8   Zvalidate_secretr   r   r   namer	   rb   r;   r   _verify_algsAssertionError)r   ra   r   fullr   r    Zcorrectfailedr   rC   otherr!   r!   r"   verify{  s4    





zscram.verify)r   )NN)N)F)N)F)__name__
__module____qualname____doc__rc   Zsetting_kwdsr
   identZdefault_salt_sizeZmax_salt_sizeZdefault_roundsZ
min_rounds
max_roundsZrounds_costrL   rd   r   classmethodr#   r+   r0   r   rG   rJ   rQ   rZ   rK   r_   rb   ri   __classcell__r!   r!   rO   r"   r      s@   A
%
 
/


)rm   logging	getLoggerrj   logZpasslib.utilsr   r   r   r   Zpasslib.utils.binaryr   r   Zpasslib.utils.compatr   r	   r
   r   Zpasslib.crypto.digestr   r   Zpasslib.utils.handlersutilshandlersr8   __all__Z	HasRoundsZ
HasRawSaltZHasRawChecksumZGenericHandlerr   r!   r!   r!   r"   <module>   s   