U
    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d
ddgZddddd
g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 )0r   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-1zsha-256zsha-512zsha-224zsha-384Nc                 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   <./venv/lib/python3.8/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>   s     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   Z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k
r   tj| Y nX |stj| ntd|kr(d }i }	|dD ]L}
|
d\}}zt
|d|	|< W q tk
r"   tj| Y qX 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   Z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 )Nr2   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   r=   r   r?   r    	to_string  s    zscram.to_stringc                    sB   |d k	r|d kst |}tt| jf |}|d k	r>| ||_|S N)AssertionErrorsuperr   using
_norm_algsdefault_algs)r   rH   r   kwdssubcls	__class__r   r    rF     s    zscram.usingc                    s   t t| jf | | j}|d k	r<|d k	r0td| |}nN|d k	rT| | }n6| jrt| j	}| ||kst
d|f ntd|| _d S )Nz+checksum & algs kwds are mutually exclusivezinvalid default algs: %rzno algs list specified)rE   r   __init__r   RuntimeErrorrG   keysZuse_defaultslistrH   rD   r;   r   )r   r   rI   Z
digest_maprK   r   r    rM   +  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	|krtd
|S )Ndictr   r   z*malformed algorithm name in scram hash: %r	   z0SCRAM limits algorithm names to 9 characters: %rz	raw bytesZdigestsr   -sha-1 must be in algorithm list of scram hash)
r)   rQ   r4   r5   ZExpectedTypeErrorr	   r   r   r7   r*   )r   r   Zrelaxedr   r>   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|krNtd|S )znormalize algs parameterc                 s   s   | ]}t |d V  qdS )r   Nr"   r#   r   r   r    r@   U  s     z#scram._norm_algs.<locals>.<genexpr>c                 s   s   | ]}t |d kV  qdS )rR   N)r7   r#   r   r   r    r@   V  s     z-SCRAM limits alg names to max of 9 charactersr   rS   )r)   r   r   sortedanyr   )r   r   r   r   r    rG   P  s    
zscram._norm_algsc                    s(   t | j| jsdS tt| jf |S )NT)setr   
issupersetrH   rE   r   _calc_needs_update)r   rI   rK   r   r    rY   `  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 rC   r   r#   r   r   r   secretr   r    r@   v  s   z'scram._calc_checksum.<locals>.<genexpr>)r   r   r,   rQ   r   )r   r[   r   r   rZ   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 ]*}||k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!)r4   Zvalidate_secretr   r   r   namer	   r\   r7   r   _verify_algsrD   )r   r[   r   Zfullr   r   ZcorrectZfailedr   r>   otherr   r   r    verify{  s4    





zscram.verify)r   )NN)N)F)N)F)__name__
__module____qualname____doc__r]   Zsetting_kwdsr
   identZdefault_salt_sizeZmax_salt_sizeZdefault_roundsZ
min_roundsZ
max_roundsZrounds_costrH   r^   r   classmethodr!   r(   r,   r   rB   rF   rM   rT   rG   rY   r\   r`   __classcell__r   r   rK   r    r      s@   A

%
 
/


)rd   ZloggingZ	getLoggerra   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.handlersZutilsZhandlersr4   __all__Z	HasRoundsZ
HasRawSaltZHasRawChecksumZGenericHandlerr   r   r   r   r    <module>   s    
