U
    hp                     @   s  d Z ddlmZmZmZmZ ddlZddlmZ zddl	m
Z
 W n  ek
r`   ddlm
Z
 Y nX ddlmZmZ ddlZeeZddlZddlZddlmZ ddlmZmZmZmZ dd	lmZmZmZ dd
l m!Z! ddddgZ"e#ddddddZ$dd Z%dd Z&d1ddZ'e(e)fZ*e+e,fZ-e+ Z.d2ddZ/G dd de0Z1e#d d!d"d#d$Z2G d%d& d&e1Z3d3d'dZ4d(d) Z5G d*d+ d+e
Z6e6 Z7d,8 D ]Z9e7:e9d-e9  qG d.d/ d/e1Z;d4d0dZ<dS )5z*passlib.pwd -- password generation helpers    )absolute_importdivisionprint_functionunicode_literalsN)defaultdict)MutableMapping)ceillog)exc)PY2irange
itervalues	int_types)rng
getrandstr
to_unicode)memoized_propertygenworddefault_charsets	genphrasedefault_wordsets      $   0   <   )ZunsafeZweakZfairstrongZsecurec                 C   s    t | j}|||d d S )z2return remaining classes in object's MRO after cls   N)type__mro__index)objclsmro r$   1./venv/lib/python3.8/site-packages/passlib/pwd.py_superclasses8   s    
r&   c                 C   s   zt | }W n tk
r$   d}Y nX tt}| D ]}||  d7  < q2|dkrb| }t|}nt|}|srdS t|dtdd |D |  S )a  
    returns 'rate of self-information' --
    i.e. average (per-symbol) entropy of the sequence **source**,
    where probability of a given symbol occurring is calculated based on
    the number of occurrences within the sequence itself.

    if all elements of the source are unique, this should equal ``log(len(source), 2)``.

    :arg source:
        iterable containing 0+ symbols
        (e.g. list of strings or ints, string of characters, etc).

    :returns:
        float bits of entropy
    Nr   r      c                 s   s   | ]}|t |d  V  qdS )r'   N)logf).0valuer$   r$   r%   	<genexpr>_   s     z"_self_info_rate.<locals>.<genexpr>)len	TypeErrorr   intvaluessumr   r(   )sourcesizeZcountscharr/   r$   r$   r%   _self_info_rate>   s    

r4   c                 C   sZ   |rt |t| S tj| r,t| dS | d\}}}|sNtd| f t	
||S )a  
    :param asset_path:
        string containing absolute path to file,
        or package-relative path using format
        ``"python.module:relative/file/path"``.

    :returns:
        filehandle opened in 'rb' mode
        (unless encoding explicitly specified)
    rb:zKasset path must be absolute file path or use 'pkg.name:sub/path' format: %r)codecs	getreader_open_asset_pathospathisabsopen	partition
ValueErrorpkg_resourcesZresource_stream)r;   encodingpackagesepZsubpathr$   r$   r%   r9   j   s    
r9   r1   c           	      C   s  t }d}z| |krW dS W n tk
r2   d}Y nX t| tsRtt| t| kr~|rzz||  W n tk
rx   Y nX dS t }t }| D ]}||kr|n|| qt|}d}t||krd}ddd |d| D }t||kr|d	t||  7 }t	d
||f dS )z
    helper for generators --
    Throws ValueError if source elements aren't unique.
    Error message will display (abbreviated) repr of the duplicates in a string/list
    TF      , c                 s   s   | ]}t t|V  qd S N)reprstrr)   Zwordr$   r$   r%   r+      s     z!_ensure_unique.<locals>.<genexpr>Nz, ... plus %d othersz*`%s` cannot contain duplicate elements: %s)
_ensure_unique_cacher-   
isinstance
_set_typesr,   setaddsortedjoinr?   )	r1   paramcacheZhashableseenZdupselemZtruncZdup_reprr$   r$   r%   _ensure_unique   s8    

rV   c                       sp   e Zd ZdZdZdZeZdZd fdd	Ze	dd Z
e	dd	 Zd
d ZdddZdd Zerhdd Z  ZS )SequenceGeneratoraY  
    Base class used by word & phrase generators.

    These objects take a series of options, corresponding
    to those of the :func:`generate` function.
    They act as callables which can be used to generate a password
    or a list of 1+ passwords. They also expose some read-only
    informational attributes.

    Parameters
    ----------
    :param entropy:
        Optionally specify the amount of entropy the resulting passwords
        should contain (as measured with respect to the generator itself).
        This will be used to auto-calculate the required password size.

    :param length:
        Optionally specify the length of password to generate,
        measured as count of whatever symbols the subclass uses (characters or words).
        Note if ``entropy`` requires a larger minimum length,
        that will be used instead.

    :param rng:
        Optionally provide a custom RNG source to use.
        Should be an instance of :class:`random.Random`,
        defaults to :class:`random.SystemRandom`.

    Attributes
    ----------
    .. autoattribute:: length
    .. autoattribute:: symbol_count
    .. autoattribute:: entropy_per_symbol
    .. autoattribute:: entropy

    Subclassing
    -----------
    Subclasses must implement the ``.__next__()`` method,
    and set ``.symbol_count`` before calling base ``__init__`` method.
    Nr   c                    s   | j d k	std|d k	s"|d krr|d kr0| j}t||}|dkrLtdtt|| j }|d ksn||k rr|}|| _|dk rtd|| _	|d k	r|| _
|rt| ttfkrtdd|  tt| jf | d S )Nzsubclass must set .symbol_countr   z!`entropy` must be positive numberr   z!`length` must be positive integerzUnexpected keyword(s): %srF   )symbol_countAssertionErrorrequested_entropyentropy_aliasesgetr?   r.   r   entropy_per_symbollengthr   r&   rW   objectr-   rQ   keyssuper__init__)selfentropyr^   r   kwdsZ
min_length	__class__r$   r%   rb      s&    zSequenceGenerator.__init__c                 C   s   t | jdS )zZ
        Average entropy per symbol (assuming all symbols have equal probability)
        r'   )r(   rX   rc   r$   r$   r%   r]     s    z$SequenceGenerator.entropy_per_symbolc                 C   s   | j | j S )a+  
        Effective entropy of generated passwords.

        This value will always be a multiple of :attr:`entropy_per_symbol`.
        If entropy is specified in constructor, :attr:`length` will be chosen so
        so that this value is the smallest multiple >= :attr:`requested_entropy`.
        )r^   r]   rh   r$   r$   r%   rd     s    	zSequenceGenerator.entropyc                 C   s   t ddS )z;main generation function, should create one password/phrasezimplement in subclassN)NotImplementedErrorrh   r$   r$   r%   __next__)  s    zSequenceGenerator.__next__c                    sN   |dkrt  S t|tr0 fddt|D S |tkr< S t|dddS )zN
        frontend used by genword() / genphrase() to create passwords
        Nc                    s   g | ]}t  qS r$   )nextr)   _rh   r$   r%   
<listcomp>4  s     z.SequenceGenerator.__call__.<locals>.<listcomp>z<None>, int, or <iter>returns)rk   rL   r   r   iterr
   ZExpectedTypeError)rc   ro   r$   rh   r%   __call__-  s    
zSequenceGenerator.__call__c                 C   s   | S rG   r$   rh   r$   r$   r%   __iter__:  s    zSequenceGenerator.__iter__c                 C   s   |   S rG   )rj   rh   r$   r$   r%   rk   >  s    zSequenceGenerator.next)NNN)N)__name__
__module____qualname____doc__r^   rZ   r   rX   rb   r   r]   rd   rj   rq   rr   r   rk   __classcell__r$   r$   rf   r%   rW      s   ,#


rW   zH0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*?/Z>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZZ2234679abcdefghjkmnpqrstuvwxyzACDEFGHJKMNPQRTUVWXYZZ0123456789abcdef)Zascii_72ascii_62Zascii_50hexc                       s>   e Zd ZdZdZdZd
 fdd	Zedd Zdd	 Z	  Z
S )WordGeneratora  
    Class which generates passwords by randomly choosing from a string of unique characters.

    Parameters
    ----------
    :param chars:
        custom character string to draw from.

    :param charset:
        predefined charset to draw from.

    :param \*\*kwds:
        all other keywords passed to the :class:`SequenceGenerator` parent class.

    Attributes
    ----------
    .. autoattribute:: chars
    .. autoattribute:: charset
    .. autoattribute:: default_charsets
    rx   Nc                    sf   |r|r,t dn|s$| j}|s$tt| }|| _t|dd}t|dd || _tt| j	f | d S )Nz,`chars` and `charset` are mutually exclusivecharsrR   )
r-   charsetrY   r   r   rV   r{   ra   rz   rb   )rc   r{   r}   re   rf   r$   r%   rb   ~  s    
zWordGenerator.__init__c                 C   s
   t | jS rG   )r,   r{   rh   r$   r$   r%   rX     s    zWordGenerator.symbol_countc                 C   s   t | j| j| jS rG   )r   r   r{   r^   rh   r$   r$   r%   rj     s    zWordGenerator.__next__)NN)rs   rt   ru   rv   r}   r{   rb   r   rX   rj   rw   r$   r$   rf   r%   rz   \  s   
rz   c                 K   s   t f || d|}||S )a
  Generate one or more random passwords.

    This function uses :mod:`random.SystemRandom` to generate
    one or more passwords using various character sets.
    The complexity of the password can be specified
    by size, or by the desired amount of entropy.

    Usage Example::

        >>> # generate a random alphanumeric string with 48 bits of entropy (the default)
        >>> from passlib import pwd
        >>> pwd.genword()
        'DnBHvDjMK6'

        >>> # generate a random hexadecimal string with 52 bits of entropy
        >>> pwd.genword(entropy=52, charset="hex")
        '310f1a7ac793f'

    :param entropy:
        Strength of resulting password, measured in 'guessing entropy' bits.
        An appropriate **length** value will be calculated
        based on the requested entropy amount, and the size of the character set.

        This can be a positive integer, or one of the following preset
        strings: ``"weak"`` (24), ``"fair"`` (36),
        ``"strong"`` (48), and ``"secure"`` (56).

        If neither this or **length** is specified, **entropy** will default
        to ``"strong"`` (48).

    :param length:
        Size of resulting password, measured in characters.
        If omitted, the size is auto-calculated based on the **entropy** parameter.

        If both **entropy** and **length** are specified,
        the stronger value will be used.

    :param returns:
        Controls what this function returns:

        * If ``None`` (the default), this function will generate a single password.
        * If an integer, this function will return a list containing that many passwords.
        * If the ``iter`` constant, will return an iterator that yields passwords.

    :param chars:

        Optionally specify custom string of characters to use when randomly
        generating a password. This option cannot be combined with **charset**.

    :param charset:

        The predefined character set to draw from (if not specified by **chars**).
        There are currently four presets available:

        * ``"ascii_62"`` (the default) -- all digits and ascii upper & lowercase letters.
          Provides ~5.95 entropy per character.

        * ``"ascii_50"`` -- subset which excludes visually similar characters
          (``1IiLl0Oo5S8B``). Provides ~5.64 entropy per character.

        * ``"ascii_72"`` -- all digits and ascii upper & lowercase letters,
          as well as some punctuation. Provides ~6.17 entropy per character.

        * ``"hex"`` -- Lower case hexadecimal.  Providers 4 bits of entropy per character.

    :returns:
        :class:`!unicode` string containing randomly generated password;
        or list of 1+ passwords if :samp:`returns={int}` is specified.
    )r^   rd   )rz   rd   r^   ro   re   genr$   r$   r%   r     s    Fc              	   C   sL   t | d&}dd |D }tdd |D }W 5 Q R X tdt||  |S )a2  
    load wordset from compressed datafile within package data.
    file should be utf-8 encoded

    :param asset_path:
        string containing  absolute path to wordset file,
        or "python.module:relative/file/path".

    :returns:
        tuple of words, as loaded from specified words file.
    zutf-8c                 s   s   | ]}|  V  qd S rG   )striprJ   r$   r$   r%   r+     s     z _load_wordset.<locals>.<genexpr>c                 s   s   | ]}|r|V  qd S rG   r$   rJ   r$   r$   r%   r+     s      z!loaded %d-element wordset from %r)r9   tupler	   debugr,   )Z
asset_pathZfhr   wordsr$   r$   r%   _load_wordset  s
    r   c                       sl   e Zd ZdZdZdZ fddZdd Zdd Zd	d
 Z	dd Z
edd Zdd Zdd Zdd Z  ZS )WordsetDictz
    Special mapping used to store dictionary of wordsets.
    Different from a regular dict in that some wordsets
    may be lazy-loaded from an asset path.
    Nc                    s"   i | _ i | _tt| j|| d S rG   )paths_loadedra   r   rb   )rc   argsre   rf   r$   r%   rb   %  s    zWordsetDict.__init__c                 C   sB   z| j | W S  tk
r    Y nX | j| }t| }| j |< |S rG   )r   KeyErrorr   r   )rc   keyr;   r*   r$   r$   r%   __getitem__*  s    
zWordsetDict.__getitem__c                 C   s   || j |< dS )z;
        set asset path to lazy-load wordset from.
        N)r   )rc   r   r;   r$   r$   r%   set_path3  s    zWordsetDict.set_pathc                 C   s   || j |< d S rG   )r   )rc   r   r*   r$   r$   r%   __setitem__9  s    zWordsetDict.__setitem__c                 C   s,   || kr | j |= | j|d  n| j|= d S rG   )r   r   poprc   r   r$   r$   r%   __delitem__<  s    zWordsetDict.__delitem__c                 C   s   t | j}|| j |S rG   )rN   r   updater   )rc   r`   r$   r$   r%   _keysetC  s    
zWordsetDict._keysetc                 C   s
   t | jS rG   )rp   r   rh   r$   r$   r%   rr   I  s    zWordsetDict.__iter__c                 C   s
   t | jS rG   )r,   r   rh   r$   r$   r%   __len__L  s    zWordsetDict.__len__c                 C   s   || j kp|| jkS rG   )r   r   r   r$   r$   r%   __contains__P  s    zWordsetDict.__contains__)rs   rt   ru   rv   r   r   rb   r   r   r   r   propertyr   rr   r   r   rw   r$   r$   rf   r%   r     s   	
r   z%eff_long eff_short eff_prefixed bip39zpasslib:_data/wordsets/%s.txtc                       sB   e Zd ZdZdZdZdZd fdd	Zedd Z	d	d
 Z
  ZS )PhraseGeneratora  class which generates passphrases by randomly choosing
    from a list of unique words.

    :param wordset:
        wordset to draw from.
    :param preset:
        name of preset wordlist to use instead of ``wordset``.
    :param spaces:
        whether to insert spaces between words in output (defaults to ``True``).
    :param \*\*kwds:
        all other keywords passed to the :class:`SequenceGenerator` parent class.

    .. autoattribute:: wordset
    Zeff_longN c                    s   |d k	r|d k	r8t dn|d kr0| j}|s0tt| }|| _t|tsPt|}t|dd || _|d krp| j	}t
|dd}|| _	tt| jf | d S )Nz,`words` and `wordset` are mutually exclusiver   r|   rC   )r-   wordsetrY   r   rL   _sequence_typesr   rV   r   rC   r   ra   r   rb   )rc   r   r   rC   re   rf   r$   r%   rb   ~  s"    

zPhraseGenerator.__init__c                 C   s
   t | jS rG   )r,   r   rh   r$   r$   r%   rX     s    zPhraseGenerator.symbol_countc                    s$    fddt  jD } j|S )Nc                 3   s   | ]} j  jV  qd S rG   )r   choicer   rl   rh   r$   r%   r+     s     z+PhraseGenerator.__next__.<locals>.<genexpr>)r   r^   rC   rQ   )rc   r   r$   rh   r%   rj     s    zPhraseGenerator.__next__)NNN)rs   rt   ru   rv   r   r   rC   rb   r   rX   rj   rw   r$   r$   rf   r%   r   _  s   "
r   c                 K   s   t f | |d|}||S )am  Generate one or more random password / passphrases.

    This function uses :mod:`random.SystemRandom` to generate
    one or more passwords; it can be configured to generate
    alphanumeric passwords, or full english phrases.
    The complexity of the password can be specified
    by size, or by the desired amount of entropy.

    Usage Example::

        >>> # generate random phrase with 48 bits of entropy
        >>> from passlib import pwd
        >>> pwd.genphrase()
        'gangly robbing salt shove'

        >>> # generate a random phrase with 52 bits of entropy
        >>> # using a particular wordset
        >>> pwd.genword(entropy=52, wordset="bip39")
        'wheat dilemma reward rescue diary'

    :param entropy:
        Strength of resulting password, measured in 'guessing entropy' bits.
        An appropriate **length** value will be calculated
        based on the requested entropy amount, and the size of the word set.

        This can be a positive integer, or one of the following preset
        strings: ``"weak"`` (24), ``"fair"`` (36),
        ``"strong"`` (48), and ``"secure"`` (56).

        If neither this or **length** is specified, **entropy** will default
        to ``"strong"`` (48).

    :param length:
        Length of resulting password, measured in words.
        If omitted, the size is auto-calculated based on the **entropy** parameter.

        If both **entropy** and **length** are specified,
        the stronger value will be used.

    :param returns:
        Controls what this function returns:

        * If ``None`` (the default), this function will generate a single password.
        * If an integer, this function will return a list containing that many passwords.
        * If the ``iter`` builtin, will return an iterator that yields passwords.

    :param words:

        Optionally specifies a list/set of words to use when randomly generating a passphrase.
        This option cannot be combined with **wordset**.

    :param wordset:

        The predefined word set to draw from (if not specified by **words**).
        There are currently four presets available:

        ``"eff_long"`` (the default)

            Wordset containing 7776 english words of ~7 letters.
            Constructed by the EFF, it offers ~12.9 bits of entropy per word.

            This wordset (and the other ``"eff_"`` wordsets)
            were `created by the EFF <https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases>`_
            to aid in generating passwords.  See their announcement page
            for more details about the design & properties of these wordsets.

        ``"eff_short"``

            Wordset containing 1296 english words of ~4.5 letters.
            Constructed by the EFF, it offers ~10.3 bits of entropy per word.

        ``"eff_prefixed"``

            Wordset containing 1296 english words of ~8 letters,
            selected so that they each have a unique 3-character prefix.
            Constructed by the EFF, it offers ~10.3 bits of entropy per word.

        ``"bip39"``

            Wordset of 2048 english words of ~5 letters,
            selected so that they each have a unique 4-character prefix.
            Published as part of Bitcoin's `BIP 39 <https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt>`_,
            this wordset has exactly 11 bits of entropy per word.

            This list offers words that are typically shorter than ``"eff_long"``
            (at the cost of slightly less entropy); and much shorter than
            ``"eff_prefixed"`` (at the cost of a longer unique prefix).

    :param sep:
        Optional separator to use when joining words.
        Defaults to ``" "`` (a space), but can be an empty string, a hyphen, etc.

    :returns:
        :class:`!unicode` string containing randomly generated passphrase;
        or list of 1+ passphrases if :samp:`returns={int}` is specified.
    )rd   r^   )r   r~   r$   r$   r%   r     s    a)N)r1   )NNN)NNN)=rv   Z
__future__r   r   r   r   r7   collectionsr   Zcollections.abcr   ImportErrorZmathr   r	   r(   ZloggingZ	getLoggerrs   r@   r:   Zpasslibr
   Zpasslib.utils.compatr   r   r   r   Zpasslib.utilsr   r   r   Zpasslib.utils.decorr   __all__dictr[   r&   r4   r9   listr   r   rN   	frozensetrM   rK   rV   r_   rW   r   rz   r   r   r   r   splitnamer   r   r   r$   r$   r$   r%   <module>   sh    
  
,

. L
M#>R