a fWcI@sdZddlmZmZmZddlmZddlZddlZddl Z ddl Z e e Z ddlZddlZddlZddlZddlZerddlmZmZmZmZn ddlmZmZddlmZmZddlmZz.dd lmZddlZ ddl!Z dd l"m#Z$[ Wn&e%ye &d dZ$ZYn0dd l'm(Z(dd l)m*Z*m+Z+m,Z,m-Z-ddl.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6ddl7m8Z8m9Z9m:Z:ddlm;Z;mZ>m?Z?m@Z@mAZAmBZBmCZCmDZDddlEmFZFmGZGddlHmIZImJZJmKZKddlLmMZMgdZNejOdkrddlmPZPdePvrePQde &d[PeRe;dejSZTgdZUddZVd2ddZWd d!ZXeYe$ZZeRd"Z[Gd#d$d$e\Z]e^d%j_Z`e^d&jaZbd'ZcGd(d)d)e\ZdGd*d+d+e4ZeGd,d-d-e4Zfd.e8dd/fd0d1ZgdS)3z@passlib.totp -- TOTP / RFC6238 / Google Authenticator utilities.)absolute_importdivisionprint_function)PY3N)urlparse parse_qslquoteunquote)rr )rr)warn)default_backend)ciphersz=can't import 'cryptography' package, totp encryption disabled)exc) TokenErrorMalformedTokenErrorInvalidTokenErrorUsedTokenError) to_unicodeto_bytesconsteq getrandbytesrng SequenceMixin xor_bytes getrandstr) BASE64_CHARS b32encode b32decode) uunicodenative_string_types bascii_to_str int_types num_typesirangebyte_elem_value UnicodeIOsuppress_cause) hybrid_methodmemoized_property) lookup_hash compile_hmac pbkdf2_hmac) pbkdf2_sha256) AppWalletTOTPrrrr TotpToken TotpMatch)) uses_queryotpauthz4registered 'otpauth' scheme with urlparse.uses_queryz\s|[-=])r3cCsLtD]}||s|Sqtd}d}tD]}|||kr*|}||}q*|S)zb helper for group_string() -- calculates optimal size of group for given string size. r) _chunk_sizes)klensizeZbestZremr;0/usr/lib/python3.9/site-packages/passlib/totp.py_get_group_sizeYs   r=-cs2t}t||fddtd|DS)z reformat string into (roughly) evenly-sized groups, separated by **sep**. useful for making tokens & keys easier to read by humans. c3s|]}||VqdSNr;).0or:valuer;r< szgroup_string..r)lenr=joinr#)rCsepr9r;rBr< group_stringlsrIcCs|dkr$t|ts t|dd|St|dd}td|d}|dksR|dkr`t | S|d krpt |St d |fd S) zR internal TOTP() helper -- decodes key according to specified format. rawbyteskeyparamzutf-8hexbase16base32 unknown byte-encoding format: %rN) isinstancerKr ExpectedTypeErrorr _clean_resubencodebase64Z b16decodeupperr ValueErrorrLformatr;r;r< _decode_bytesys  r^z(?i)^[a-z0-9][a-z0-9_.-]*$c@sleZdZdZdZdZdZdZdddZddZ d d Z e d d Z d dZ edddZddZddZdS)r-a This class stores application-wide secrets that can be used to encrypt & decrypt TOTP keys for storage. It's mostly an internal detail, applications usually just need to pass ``secrets`` or ``secrets_path`` to :meth:`TOTP.using`. .. seealso:: :ref:`totp-storing-instances` for more details on this workflow. Arguments ========= :param secrets: Dict of application secrets to use when encrypting/decrypting stored TOTP keys. This should include a secret to use when encrypting new keys, but may contain additional older secrets to decrypt existing stored keys. The dict should map tags -> secrets, so that each secret is identified by a unique tag. This tag will be stored along with the encrypted key in order to determine which secret should be used for decryption. Tag should be string that starts with regex range ``[a-z0-9]``, and the remaining characters must be in ``[a-z0-9_.-]``. It is recommended to use something like a incremental counter ("1", "2", ...), an ISO date ("2016-01-01", "2016-05-16", ...), or a timestamp ("19803495", "19813495", ...) when assigning tags. This mapping be provided in three formats: * A python dict mapping tag -> secret * A JSON-formatted string containing the dict * A multiline string with the format ``"tag: value\ntag: value\n..."`` (This last format is mainly useful when loading from a text file via **secrets_path**) .. seealso:: :func:`generate_secret` to create a secret with sufficient entropy :param secrets_path: Alternately, callers can specify a separate file where the application-wide secrets are stored, using either of the string formats described in **secrets**. :param default_tag: Specifies which tag in **secrets** should be used as the default for encrypting new keys. If omitted, the tags will be sorted, and the largest tag used as the default. if all tags are numeric, they will be sorted numerically; otherwise they will be sorted alphabetically. this permits tags to be assigned numerically, or e.g. using ``YYYY-MM-DD`` dates. :param encrypt_cost: Optional time-cost factor for key encryption. This value corresponds to log2() of the number of PBKDF2 rounds used. .. warning:: The application secret(s) should be stored in a secure location by your application, and each secret should contain a large amount of entropy (to prevent brute-force attacks if the encrypted keys are leaked). :func:`generate_secret` is provided as a convenience helper to generate a new application secret of suitable size. Best practice is to load these values from a file via **secrets_path**, and then have your application give up permission to read this file once it's running. Public Methods ============== .. autoattribute:: has_secrets .. autoattribute:: default_tag Semi-Private Methods ==================== The following methods are used internally by the :class:`TOTP` class in order to encrypt & decrypt keys using the provided application secrets. They will generally not be publically useful, and may have their API changed periodically. .. automethod:: get_secret .. automethod:: encrypt_key .. automethod:: decrypt_key NcCs|dur,t|trt|}|dks&J||_|durR|durDtdt|d}||}|_|r|durz| |n(t dd|Drt |td}nt |}||_ dS)Nrz3'secrets' and 'secrets_path' are mutually exclusiveZrtcss|]}|VqdSr?)isdigit)r@tagr;r;r<rD)rEz%AppWallet.__init__..)rL) rTrint encrypt_cost TypeErroropenread_parse_secrets_secrets get_secretallmax default_tag)selfsecretsrmrdZ secrets_pathr;r;r<__init__s"    zAppWallet.__init__csd}t|trV|dr(t|}n.d|vrNd|vrNdd}||}d}ntd|d urbiSt|trv|}n |rt d tfd d |DS) zf parse 'secrets' parameter :returns: Dict[tag:str, secret:bytes] T)[{ :cssJ|D]<}|}|r|ds|dd\}}||fVqdS)N#rt) splitlinesstrip startswithsplit)sourcelinerbsecretr;r;r< iter_pairsAs  z,AppWallet._parse_secrets..iter_pairsFz"unrecognized secrets string formatNz+'secrets' must be mapping, or list of itemsc3s|]\}}||VqdSr?)_parse_secret_pair)r@rbrCrnr;r<rDWsz+AppWallet._parse_secrets..) rTrlstripryjsonloadsr[dictitemsre)rnr{Z check_typer~r;rr<rh/s$      zAppWallet._parse_secretscCs|t|tr n"t|tr t|}ntd|ft|sFtd|ft|tsbt |d|fd}|sttd|f||fS)Nztag must be unicode/string: %rz#tag contains invalid characters: %rz secret %rrMztag contains empty secret: %r) rTrrcstrre_tag_rematchr[rKr)rnrbrCr;r;r<rZs     zAppWallet._parse_secret_paircCs |jduS)z2whether at least one application secret is presentN)rmrr;r;r< has_secretsmszAppWallet.has_secretscCsF|j}|stdz ||WSty@ttd|fYn0dS)zh resolve a secret tag to the secret (as bytes). throws a KeyError if not found. z!no application secrets configuredzunknown secret tag: %rN)riKeyErrorr&)rnrbror;r;r<rjrs  zAppWallet.get_secretFcCs|tdurtdtd||d|>dd}ttj|ddtj|ddt}|rb| n| }| || S)a& Internal helper for :meth:`encrypt_key` -- handles lowlevel encryption/decryption. Algorithm details: This function uses PBKDF2-HMAC-SHA256 to generate a 32-byte AES key and a 16-byte IV from the application secret & random salt. It then uses AES-256-CTR to encrypt/decrypt the TOTP key. CTR mode was chosen over CBC because the main attack scenario here is that the attacker has stolen the database, and is trying to decrypt a TOTP key (the plaintext value here). To make it hard for them, we want every password to decrypt to a potentially valid key -- thus need to avoid any authentication or padding oracle attacks. While some random padding construction could be devised to make this work for CBC mode, a stream cipher mode is just plain simpler. OFB/CFB modes would also work here, but seeing as they have malleability and cyclic issues (though remote and barely relevant here), CTR was picked as the best overall choice. NzITOTP encryption requires 'cryptography' package (https://cryptography.io)Zsha256rv0)saltroundsZkeylen ) _cg_ciphers RuntimeErrorr+ZCipherZ algorithmsZAESZmodesZCTR_cg_default_backendZ decryptorZ encryptorupdatefinalize)rCr}rcostZdecryptZkeyivZcipherctxr;r;r<_cipher_aes_keyszAppWallet._cipher_aes_keycCs`|s tdtt|j}|j}|j}|s0td||||||}t d||t |t |dS)a0 Helper used to encrypt TOTP keys for storage. :param key: TOTP key to encrypt, as raw bytes. :returns: dict containing encrypted TOTP key & configuration parameters. this format should be treated as opaque, and potentially subject to change, though it is designed to be easily serialized/deserialized (e.g. via JSON). .. note:: This function requires installation of the external `cryptography `_ package. To give some algorithm details: This function uses AES-256-CTR to encrypt the provided data. It takes the application secret and randomly generated salt, and uses PBKDF2-HMAC-SHA256 to combine them and generate the AES key & IV. zno key providedz8no application secrets configured, can't encrypt OTP keyrv)vctsk) r[rr salt_sizerdrmrerrjrr)rnrLrrrbZckeyr;r;r< encrypt_keys zAppWallet.encrypt_keycCst|tstd|dd}d}|dkr2|j}ntd|f|d}|d}|t|d ||t|d |d }||jks||j krd }||fS) a Helper used to decrypt TOTP keys from storage format. Consults configured secrets to decrypt key. :param source: source object, as returned by :meth:`encrypt_key`. :returns: ``(key, needs_recrypt)`` -- **key** will be the decrypted key, as bytes. **needs_recrypt** will be a boolean flag indicating whether encryption cost or default tag is too old, and henace that key needs re-encrypting before storing. .. note:: This function requires installation of the external `cryptography `_ package. z'enckey' must be dictionaryrNFrvz+missing / unrecognized 'enckey' version: %rrrrr)rCr}rrT) rTrregetrr[rrjrdrm)rnenckeyversion needs_recryptZ _cipher_keyrbrrLr;r;r< decrypt_keys$    zAppWallet.decrypt_key)NNNN)F)__name__ __module__ __qualname____doc__rrdrirmrprhrpropertyrrj staticmethodrrrr;r;r;r<r-s"] )+  %!r-z>Qz>Isc seZdZdZdZdZZdZej Z dZ dZ dZ dZdZdZdZdZdZedTd d Zed d ZdUfdd ZedVddZeddZeddZeddZejddZeddZejddZeddZ edd Z!dWd"d#Z"ed$d%Z#d&d'Z$d(d)Z%e&d*d+Z'dXd,d-Z(d.d/Z)ed0d1Z*dYd2d3Z+dZd4d5Z,ed6d7Z-ed8d9Z.ed:d;Z/edd?Z1ed@dAZ2edBdCZ3d\dDdEZ4dFdGZ5edHdIZ6d]dJdKZ7edLdMZ8edNdOZ9edPdQZ:d^dRdSZ;Z>> # your application can create a custom class when it initializes >>> from passlib.totp import TOTP, generate_secret >>> TotpFactory = TOTP.using(secrets={"1": generate_secret()}) >>> # subsequent TOTP objects created from this factory >>> # will use the specified secrets to encrypt their keys... >>> totp = TotpFactory.new() >>> totp.to_dict() {'enckey': {'c': 14, 'k': 'H77SYXWORDPGVOQTFRR2HFUB3C45XXI7', 's': 'G5DOQPIHIBUM2OOHHADQ', 't': '1', 'v': 1}, 'type': 'totp', 'v': 1} .. seealso:: :ref:`totp-creation` and :ref:`totp-storing-instances` tutorials for a usage example r.cs,ttdd}|||<fi|}t||S)z helper which uses constructor to validate parameter value. it returns corresponding attribute, so we use normalized value. rJr\)r _DUMMY_KEYgetattr)attrrCkwdsobjsubclsr;r< norm_params zTOTP.using..norm_paramNdigitsalgperiodissuerz6'wallet' and 'secrets' keywords are mutually exclusivewalletrz1now() function must return non-negative int/float)typerrrrr-rrerTr rUr"rnow) clsrrrrrrrrr;rr<usings08        z TOTP.usingcKs|fddi|S)zY convenience alias for creating new TOTP key, same as ``TOTP(new=True)`` newTr;)rrr;r;r<rszTOTP.newrRc  stt|jfi| | r | |_t|p*|j} | j|_| j} | dkrPtd||r|r`t d|durn| }n|| krt d| t t ||_ n.|st dn |dkr||_n|rt|||_ t|j |jkrd|j}|rt |nt|tjdd |dur|j}t|ts"t d t||d ks6|d kr>t d ||_|rZ||||_| rp|| | |_|dur|j|ddd||_dS)Nr3z%r hash digest too smallz+'key' and 'new=True' are mutually exclusivez+'size' should be less than digest size (%d)z4must specify either an existing 'key', or 'new=True' encryptedz5for security purposes, secret key must be >= %d bytesrv) stacklevelz#digits must be an integer, not a %rr6rzdigits must in range(6,11)r)minval)superr.rpchangedr)rname digest_sizerrer[rrrL encrypted_keyr^rF _min_key_sizer r ZPasslibSecurityWarningrrTr!r _check_labellabel _check_issuerr _check_serialr)rnrLr]rrrr:rrrrrinformsg __class__r;r<rpsZ          z TOTP.__init__rcCs4t|tst|d|||kr0td||fdS)zR check that serial value (e.g. 'counter') is non-negative integer rcz%s must be >= %dN)rTr!r rUr[)rCrNrr;r;r<rOs zTOTP._check_serialcCs|rd|vrtddS)zQ check that label doesn't contain chars forbidden by KeyURI spec rtzlabel may not contain ':'Nr[rr;r;r<rYs zTOTP._check_labelcCs|rd|vrtddS)zR check that issuer doesn't contain chars forbidden by KeyURI spec rtzissuer may not contain ':'Nr)rr;r;r<ras zTOTP._check_issuercCs|jS)z) secret key as raw bytes )_keyrr;r;r<rLpszTOTP.keycCs.t|tst|td||_d|_|_dS)NrL)rTrKr rUr_encrypted_key _keyed_hmac)rnrCr;r;r<rLws cCs6|j}|dur2|j}|s td||j}|_|S)z secret key, encrypted using application secret. this match the output of :meth:`AppWallet.encrypt_key`, and should be treated as an opaque json serializable object. Nz6no application secrets present, can't encrypt TOTP key)rrrerrL)rnrrr;r;r<rszTOTP.encrypted_keycCs8|j}|std||\|_}|r.d|_n||_dS)Nz6no application secrets present, can't decrypt TOTP keyT)rrerrLrr)rnrCrrr;r;r<rscCstt|jS)z: secret key encoded as hexadecimal string )r rYZ b16encoderLlowerrr;r;r<hex_keysz TOTP.hex_keycCs t|jS)z5 secret key encoded as base32 string )rrLrr;r;r< base32_keyszTOTP.base32_keyr>cCsH|dks|dkr|j}n|dkr(|j}ntd|f|rDt||}|S)a pretty-print the secret key. This is mainly useful for situations where the user cannot get the qrcode to work, and must enter the key manually into their TOTP client. It tries to format the key in a manner that is easier for humans to read. :param format: format to output secret key. ``"hex"`` and ``"base32"`` are both accepted. :param sep: separator to insert to break up key visually. can be any of ``"-"`` (the default), ``" "``, or ``False`` (no separator). :return: key as native string. Usage example:: >>> t = TOTP('s3jdvb7qd2r7jpxx') >>> t.pretty_key() 'S3JD-VB7Q-D2R7-JPXX' rPrQrRrS)rrr[rI)rnr]rHrLr;r;r< pretty_keys zTOTP.pretty_keycCs^t|tr|St|tr t|S|dur4t|St|drLt|St |dddS)as Normalize time value to unix epoch seconds. :arg time: Can be ``None``, :class:`!datetime`, or unix epoch timestamp as :class:`!float` or :class:`!int`. If ``None``, uses current system time. Naive datetimes are treated as UTC. :returns: unix epoch timestamp as :class:`int`. N utctimetuplezint, float, or datetimetime) rTr!floatrcrhasattrcalendarZtimegmrr rU)rrr;r;r<normalize_times    zTOTP.normalize_timecCs ||jS)zI convert timestamp to HOTP counter using :attr:`period`. r)rnrr;r;r<_time_to_counterszTOTP._time_to_countercCs ||jS)zI convert HOTP counter to timestamp using :attr:`period`. r)rncounterr;r;r<_counter_to_timeszTOTP._counter_to_timecCsj|j}t|tr"td||f}n,t|dd}ttd|}|sNtdt ||krftd||S)a Normalize OTP token representation: strips whitespace, converts integers to a zero-padded string, validates token content & number of digits. This is a hybrid method -- it can be called at the class level, as ``TOTP.normalize_token()``, or the instance level as ``TOTP().normalize_token()``. It will normalize to the instance-specific number of :attr:`~TOTP.digits`, or use the class default. :arg token: token as ascii bytes, unicode, or an integer. :raises ValueError: if token has wrong number of digits, or contains non-numeric characters. :returns: token as :class:`!unicode` string, containing only digits 0-9. %0*dtokenrMrOz&Token must contain only the digits 0-9z!Token must have exactly %d digits) rrTr!rrrVrWrarrF)Z self_or_clsrrr;r;r<normalize_tokens    zTOTP.normalize_tokencCs:||}||}|dkr$td||}t|||S)a Generate token for specified time (uses current time if none specified). :arg time: Can be ``None``, a :class:`!datetime`, or class:`!float` / :class:`!int` unix epoch timestamp. If ``None`` (the default), uses current system time. Naive datetimes are treated as UTC. :returns: A :class:`TotpToken` instance, which can be treated as a sequence of ``(token, expire_time)`` -- see that class for more details. Usage example:: >>> # generate a new token, wrapped in a TotpToken instance... >>> otp = TOTP('s3jdvb7qd2r7jpxx') >>> otp.generate(1419622739) >>> # when you just need the token... >>> otp.generate(1419622739).token '897212' rztimestamp must be >= 0)rrr[ _generater/)rnrrrr;r;r<generate.s    z TOTP.generatecCst|tsJd|dks"Jd|j}|durDt|j|j}|_|t|}|jj}t ||kslJd|dks|Jdt |dd @}t |||d dd @}|j }d|krd ksnJd t d||f| dS)z base implementation of HOTP token generation algorithm. :arg counter: HOTP counter, as non-negative integer :returns: token as unicode string zcounter must be integerrzcounter must be non-negativeNz digest_size: sanity check failedz"digest_size: sanity check 2 failedr3i zdigits: sanity check failedr)rTr!rr*rrL _pack_uint64Z digest_inforrFr$_unpack_uint32rr)rnrZ keyed_hmacdigestroffsetrCrr;r;r<rQs zTOTP._generatecKs||j|fi|S)a Convenience wrapper around :meth:`TOTP.from_source` and :meth:`TOTP.match`. This parses a TOTP key & configuration from the specified source, and tries and match the token. It's designed to parallel the :meth:`passlib.ifc.PasswordHash.verify` method. :param token: Token string to match. :param source: Serialized TOTP key. Can be anything accepted by :meth:`TOTP.from_source`. :param \\*\\*kwds: All additional keywords passed to :meth:`TOTP.match`. :return: A :class:`TotpMatch` instance, or raises a :exc:`TokenError`. ) from_sourcer)rrr{rr;r;r<verifyssz TOTP.verifyc Cs||}||d||}|dur*d}t||||}|||d}||||} | |ksnJd| |krt|d|jdt|| ||S)aP Match TOTP token against specified timestamp. Searches within a window before & after the provided time, in order to account for transmission delay and small amounts of skew in the client's clock. :arg token: Token to validate. may be integer or string (whitespace and hyphens are ignored). :param time: Unix epoch timestamp, can be any of :class:`!float`, :class:`!int`, or :class:`!datetime`. if ``None`` (the default), uses current system time. *this should correspond to the time the token was received from the client*. :param int window: How far backward and forward in time to search for a match. Measured in seconds. Defaults to ``30``. Typically only useful if set to multiples of :attr:`period`. :param int skew: Adjust timestamp by specified value, to account for excessive client clock skew. Measured in seconds. Defaults to ``0``. Negative skew (the common case) indicates transmission delay, and/or that the client clock is running behind the server. Positive skew indicates the client clock is running ahead of the server (and by enough that it cancels out any negative skew added by the transmission delay). You should ensure the server clock uses a reliable time source such as NTP, so that only the client clock's inaccuracy needs to be accounted for. This is an advanced parameter that should usually be left at ``0``; The **window** parameter is usually enough to account for any observed transmission delay. :param last_counter: Optional value of last counter value that was successfully used. If specified, verify will never search earlier counters, no matter how large the window is. Useful when client has previously authenticated, and thus should never provide a token older than previously verified value. :raises ~passlib.exc.TokenError: If the token is malformed, fails to match, or has already been used. :returns TotpMatch: Returns a :class:`TotpMatch` instance on successful match. Can be treated as tuple of ``(counter, time)``. Raises error if token is malformed / can't be verified. Usage example:: >>> totp = TOTP('s3jdvb7qd2r7jpxx') >>> # valid token for this time period >>> totp.match('897212', 1419622729) >>> # token from counter step 30 sec ago (within allowed window) >>> totp.match('000492', 1419622729) >>> # invalid token -- token from 60 sec ago (outside of window) >>> totp.match('760389', 1419622729) Traceback: ... InvalidTokenError: Token did not match windowNrrvz*sanity check failed: counter went backward) expire_time)rrrlr _find_matchrrr0) rnrrrZskew last_counterZ client_timestartendrr;r;r<rsK  z TOTP.matchcCs~||}|dkrd}||kr$t|j}|dusL||ksLt|||rL|S|}||krtt|||rj|S|d7}qPtdS)aw helper for verify() -- returns counter value within specified range that matches token. :arg token: token value to match (will be normalized internally) :arg start: starting counter value to check :arg end: check up to (but not including) this counter value :arg expected: optional expected value where search should start, to help speed up searches. :raises ~passlib.exc.TokenError: If the token is malformed, or fails to verify. :returns: counter value that matched rNrv)rrrr)rnrrrZexpectedrrr;r;r<rs  zTOTP._find_matchcCsht|tr&|j|jkr|S|jdd}t|tr:||St|dd}|drZ||S| |SdS)a Load / create a TOTP object from a serialized source. This acts as a wrapper for the various deserialization methods: * TOTP URIs are handed off to :meth:`from_uri` * Any other strings are handed off to :meth:`from_json` * Dicts are handed off to :meth:`from_dict` :param source: Serialized TOTP object. :raises ValueError: If the key has been encrypted, but the application secret isn't available; or if the string cannot be recognized, parsed, or decoded. See :meth:`TOTP.using()` for how to configure application secrets. :returns: a :class:`TOTP` instance. Fencryptz totp sourcerMz otpauth://N) rTr.rto_dictr from_dictrryfrom_uri from_jsonrr{r;r;r<r%s        zTOTP.from_sourcecCsBt|dd}t|}|jdkr,|d||j||S)a< create an OTP instance from a URI (such as returned by :meth:`to_uri`). :returns: :class:`TOTP` instance. :raises ValueError: if the uri cannot be parsed or contains errors. .. seealso:: :ref:`totp-configuring-clients` tutorial for a usage example urirMr5zwrong uri scheme)rrxrscheme_uri_parse_error_check_otp_typenetloc_from_parsed_uri)rrresultr;r;r<rMs    z TOTP.from_uricCs,|dkr dS|dkrtdtd|dS)zg validate otp URI type is supported. returns True or raises appropriate error. totpTZhotpzHOTP not supportedzunknown otp type: %rN)NotImplementedErrorr[)rrr;r;r<rds zTOTP._check_otp_typecCs|j}|dr.t|dkr.t|dd}n |dd|vrrz|d\}}Wqvtyn|dYqv0nd}|r|pd}t|d}t |j D]&\}}||vr|d||||<q|rd |vr||d <n|d |kr|d |fi|j fi|S) z internal from_uri() helper -- handles parsing a validated TOTP URI :param result: a urlparse() instance :returns: cls instance /rvNz missing labelrtzmalformed labelrzduplicate parameter (%r)rzconflicting issuer identifiers) pathryrFr rrzr[rxrrquery_adapt_uri_params)rrrrparamsrrr;r;r<rps.        zTOTP._from_parsed_uric Ksz|s Jd|s|dt|||dd}|r>||d|d<|rJ||d<|r^||d|d<|rvtd||ftj|S) zY from_uri() helper -- converts uri params into constructor args. z"from_uri() failed to provide labelzmissing 'secret' parameterrR)rrrLr]rrrz4%s: unexpected parameters encountered in otp uri: %r)rr_uri_parse_intr r ZPasslibRuntimeWarning) rrr}rr algorithmrZextrarr;r;r<r s"  zTOTP._adapt_uri_paramscCstd|fS)z8uri parsing helper -- creates preformatted error messagezInvalid otpauth uri: %srreasonr;r;r<rszTOTP._uri_parse_errorcCs0z t|WSty*|d|Yn0dS)z#uri parsing helper -- int() wrapperzMalformed %r parameterN)rcr[r)rr{rNr;r;r<r s  zTOTP._uri_parse_intcCs|dur|j}|std||t|d}|}|durD|j}|rr||dt|d|f}|d|ftd dd|D}|sJd td ||fS) a Serialize key and configuration into a URI, per Google Auth's `KeyUriFormat `_. :param str label: Label to associate with this token when generating a URI. Displayed to user by most OTP client applications (e.g. Google Authenticator), and typically has format such as ``"John Smith"`` or ``"jsmith@webservice.example.org"``. Defaults to **label** constructor argument. Must be provided in one or the other location. May not contain ``:``. :param str issuer: String identifying the token issuer (e.g. the domain or canonical name of your service). Optional but strongly recommended if you're rendering to a URI. Used internally by some OTP client applications (e.g. Google Authenticator) to distinguish entries which otherwise have the same label. Defaults to **issuer** constructor argument, or ``None``. May not contain ``:``. :raises ValueError: * if a label was not provided either as an argument, or in the constructor. * if the label or issuer contains invalid characters. :returns: all the configuration information for this OTP token generator, encoded into a URI. These URIs are frequently converted to a QRCode for transferring to a TOTP client application such as Google Auth. Usage example:: >>> from passlib.totp import TOTP >>> tp = TOTP('s3jdvb7qd2r7jpxx') >>> uri = tp.to_uri("user@example.org", "myservice.another-example.org") >>> uri 'otpauth://totp/user@example.org?secret=S3JDVB7QD2R7JPXX&issuer=myservice.another-example.org' .. versionchanged:: 1.7.2 This method now prepends the issuer URI label. This is recommended by the KeyURI specification, for compatibility with older clients. Nz.zparam_str should never be emptyzotpauth://totp/%s?%s) rr[rr_to_uri_paramsrrappendrrG)rnrrr Z param_strr;r;r<to_uris .    z TOTP.to_uricCsjd|jfg}|jdkr*|d|jf|jdkrH|dt|jf|jdkrf|dt|jf|S)z+return list of (key, param) entries for URIr}rr r6rrr)rrrrZrrrrnargsr;r;r<rs    zTOTP._to_uri_paramscCst|dd}|t|S)an Load / create an OTP object from a serialized json string (as generated by :meth:`to_json`). :arg json: Serialized output from :meth:`to_json`, as unicode or ascii bytes. :raises ValueError: If the key has been encrypted, but the application secret isn't available; or if the string cannot be recognized, parsed, or decoded. See :meth:`TOTP.using()` for how to configure application secrets. :returns: a :class:`TOTP` instance. .. seealso:: :ref:`totp-storing-instances` tutorial for a usage example z json sourcerM)rrrrrr;r;r<r&s zTOTP.from_jsoncCs|j|d}tj|dddS)a Serialize configuration & internal state to a json string, mainly useful for persisting client-specific state in a database. All keywords passed to :meth:`to_dict`. :returns: json string containing serializes configuration & state. rT),rt)Z sort_keysZ separators)rrdumps)rnrstater;r;r<to_json=s z TOTP.to_jsoncCs6t|trd|vr|d|fi|jfi|S)aU Load / create a TOTP object from a dictionary (as generated by :meth:`to_dict`) :param source: dict containing serialized TOTP key & configuration. :raises ValueError: If the key has been encrypted, but the application secret isn't available; or if the dict cannot be recognized, parsed, or decoded. See :meth:`TOTP.using()` for how to configure application secrets. :returns: A :class:`TOTP` instance. .. seealso:: :ref:`totp-storing-instances` tutorial for a usage example rzunrecognized format)rTr_dict_parse_error_adapt_dict_kwdsrr;r;r<rMs zTOTP.from_dictcKs||sJ|dd}|r2||jks2||jkrD|d|fn||jkrVd|d<d|vrd|vsjJ|j|ddd nd|vr|d |d d|S) zt Internal helper for .from_json() -- Adapts serialized json dict into constructor keywords. rNz missing/unsupported version (%r)TrrrLrr\zmissing 'enckey' / 'key'r)rpopmin_json_version json_versionrr)rrrZverr;r;r<res      zTOTP._adapt_dict_kwdscCstd|fS)z9dict parsing helper -- creates preformatted error messagezInvalid totp data: %srrr;r;r<rszTOTP._dict_parse_errorcCst|jdd}|jdkr"|j|d<|jdkr6|j|d<|jdkrJ|j|d<|jrZ|j|d <|j}|rz|t|jkrz||d <|d ur|j}|o|j }|r|j |d <n |j |d <|S)aw Serialize configuration & internal state to a dict, mainly useful for persisting client-specific state in a database. :param encrypt: Whether to output should be encrypted. * ``None`` (the default) -- uses encrypted key if application secrets are available, otherwise uses plaintext key. * ``True`` -- uses encrypted key, or raises TypeError if application secret wasn't provided to OTP constructor. * ``False`` -- uses raw key. :returns: dictionary, containing basic (json serializable) datatypes. r)rrrrr6rrrrrNrrL) rrrrrrrrrrrr)rnrrrrr;r;r<rs&          z TOTP.to_dict)NNNNNN) NrRFNNNNNNF)r)rRr>)N)NrrN)N)NNNNNN)NN)N)N)=rrrrrrrr_timerrrrrrrrrrr classmethodrrrprrrrrrLsetterrrrrrrrr'rrrrrrrrrrr rr rrrrrrrr __classcell__r;r;rr<r.sM   h M          &  - #"  b 8 '   0   M     r.c@sdeZdZdZdZdZdZddZeddZ eddZ e d d Z e d d Z d dZddZdS)r/a@ Object returned by :meth:`TOTP.generate`. It can be treated as a sequence of ``(token, expire_time)``, or accessed via the following attributes: .. autoattribute:: token .. autoattribute:: expire_time .. autoattribute:: counter .. autoattribute:: remaining .. autoattribute:: valid NcCs||_||_||_dSzu .. warning:: the constructor signature is an internal detail, and is subject to change. N)rrr)rnrrrr;r;r<rpszTotpToken.__init__cCs|j|jS)z9Timestamp marking beginning of period when token is validrrrrr;r;r< start_timeszTotpToken.start_timecCs|j|jdSz3Timestamp marking end of period when token is validrvr%rr;r;r<rszTotpToken.expire_timecCstd|j|jS)z.number of (float) seconds before token expiresr)rlrrrrr;r;r< remainingszTotpToken.remainingcCs t|jS)zwhether token is still valid)boolr(rr;r;r<validszTotpToken.validcCs |j|jfSr?)rrrr;r;r< _as_tupleszTotpToken._as_tuplecCs |jr dnd}d|j|j|fS)NrOz expiredz')r(rr)rnZexpiredr;r;r<__repr__s zTotpToken.__repr__)rrrrrrrrpr(r&rrr(r*r+r,r;r;r;r<r/s      r/c@sveZdZdZdZdZdZdZdddZe ddZ e d d Z e d d Z e d dZ e ddZddZddZdS)r0a Object returned by :meth:`TOTP.match` and :meth:`TOTP.verify` on a successful match. It can be treated as a sequence of ``(counter, time)``, or accessed via the following attributes: .. autoattribute:: counter :annotation: = 0 .. autoattribute:: time :annotation: = 0 .. autoattribute:: expected_counter :annotation: = 0 .. autoattribute:: skipped :annotation: = 0 .. autoattribute:: expire_time :annotation: = 0 .. autoattribute:: cache_seconds :annotation: = 60 .. autoattribute:: cache_time :annotation: = 0 This object will always have a ``True`` boolean value. NrrcCs||_||_||_||_dSr$)rrrr)rnrrrrr;r;r<rp&szTotpMatch.__init__cCs|j|jS)z7 Counter value expected for timestamp. )rrrrr;r;r<expected_counter0szTotpMatch.expected_countercCs |j|jS)z How many steps were skipped between expected and actual matched counter value (may be positive, zero, or negative). )rr-rr;r;r<skipped7szTotpMatch.skippedcCs|j|jdSr'r%rr;r;r<rDszTotpMatch.expire_timecCs|jj|jS)z Number of seconds counter should be cached before it's guaranteed to have passed outside of verification window. )rrrrr;r;r< cache_secondsIszTotpMatch.cache_secondscCs |j|jS)z[ Timestamp marking when counter has passed outside of verification window. )rrrr;r;r< cache_timeSszTotpMatch.cache_timecCs |j|jfSr?)rrrr;r;r<r+ZszTotpMatch._as_tuplecCs|j|j|jf}d|S)Nz/)rrr/rr;r;r<r,]szTotpMatch.__repr__)r)rrrrrrrrrpr(r-r.rr/r0r+r,r;r;r;r<r0s$      r0c CsF|dks Jt|dksJtt|tdt|}tt||S)z generate a random string suitable for use as an :class:`AppWallet` application secret. :param entropy: number of bits of entropy (controls size/complexity of password). rrvr1)rFrcmathZceillogrr)Zentropycharsetcountr;r;r<generate_secretes r7)r>)hrZ __future__rrrZpasslib.utils.compatrrYrrZloggingZ getLoggerrr4r3structsysrr re urllib.parserrrr Zurllibwarningsr Zcryptography.hazmat.backendsr rZ1cryptography.hazmat.primitives.ciphers.algorithmsZ cryptographyZ,cryptography.hazmat.primitives.ciphers.modesZcryptography.hazmat.primitivesr r ImportErrordebugZpasslibr Z passlib.excrrrrZ passlib.utilsrrrrrrrrZpasslib.utils.binaryrrrrrrr r!r"r#r$r%r&Zpasslib.utils.decorr'r(Zpasslib.crypto.digestr)r*r+Z passlib.hashr,__all__ version_infor4rcompileUrVr8r=rIr^r)Z AES_SUPPORTrobjectr-Structpackrunpackrrr.r/r0r7r;r;r;r<s      (0         g  B;l