
    Yf]                         d Z ddlZddlmZ ddlmZmZ ddlmZ ddl	m
Z
mZmZmZmZ ddlmZ ddlmZ dd	lmZ dd
lmZ d Z G d de
          Z G d de          ZdS )z[
Tests for L{twisted.cred._digest} and the associated bits in
L{twisted.cred.credentials}.
    N)hexlify)md5sha1)verifyObject)DigestCredentialFactoryIUsernameDigestHashcalcHA1calcHA2calcResponse)LoginFailed)IPv4Address)networkString)TestCasec                 N    t          j        |                                           S )N)base64	b64encodestrip)ss    S/var/www/html/env/lib/python3.11/site-packages/twisted/cred/test/test_digestauth.pyr   r      s    A$$&&&    c                   .     e Zd ZdZ fdZd Zd Z xZS )FakeDigestCredentialFactoryz\
    A Fake Digest Credential Factory that generates a predictable
    nonce and opaque
    c                 H     t                      j        |i | d| _        d S )N   0)super__init__
privateKey)selfargskwargs	__class__s      r   r   z$FakeDigestCredentialFactory.__init__'   s*    $)&)))r   c                     dS )z)
        Generate a static nonce
        s   178288758716122392881254770685 r   s    r   _generateNoncez*FakeDigestCredentialFactory._generateNonce+   s
     10r   c                     dS )z&
        Return a stable time
        r   r#   r$   s    r   _getTimez$FakeDigestCredentialFactory._getTime1   s	     qr   )__name__
__module____qualname____doc__r   r%   r'   __classcell__)r!   s   @r   r   r   !   s`         
    1 1 1      r   r   c                      e Zd ZdZd ZdefdZd Zd ZdefdZ	defdZ
d	 Zd
 Zd Zd ZdefdZd Zd ZdefdZd Zd Zd*dZd Zd*dZd Zd Zd Zd Zd Zd Zd Zd Zd Z d  Z!d! Z"d" Z#d# Z$d$ Z%d% Z&d& Z'd' Z(d( Z)d)S )+DigestAuthTestsz
    L{TestCase} mixin class which defines a number of tests for
    L{DigestCredentialFactory}.  Because this mixin defines C{setUp}, it
    must be inherited before L{TestCase}.
    c                     d| _         d| _        d| _        d| _        d| _        d| _        d| _        t          dd	d
          | _        d| _	        t          | j        | j                  | _        dS )z>
        Create a DigestCredentialFactory for testing
           foobars   bazquuxs
   test realm   md5s    29fc54aa1641c6fa0e151419361c8f23   auths   /write/TCPz10.2.3.4iu     GETN)usernamepasswordrealm	algorithmcnonceqopurir   clientAddressmethodr   credentialFactoryr$   s    r   setUpzDigestAuthTests.setUp?   sl     """
9(
EBB!8!T!Tr   r1   c                 (   d}t          || j        | j        | j        || j                  }d                    | j        | j        | j        f          }t           ||                                                    }|                     ||           dS )z
        L{calcHA1} accepts the C{'md5'} algorithm and returns an MD5 hash of
        its parameters, excluding the nonce and cnonce.
        s	   abc123xyz   :N)	r	   r5   r7   r6   r9   joinr   digestassertEqual)r   
_algorithm_hashnoncehashA1a1expecteds          r   test_MD5HashA1zDigestAuthTests.test_MD5HashA1N   s    
 tz4=%
 
 YYtz4=ABB5599++--..*****r   c                    d}t          d| j        | j        | j        || j                  }| j        dz   | j        z   dz   | j        z   }t          t          |                                                    }|dz   |z   dz   | j        z   }t          t          |                                                    }|                     ||           dS )z
        L{calcHA1} accepts the C{'md5-sess'} algorithm and returns an MD5 hash
        of its parameters, including the nonce and cnonce.
        s	   xyz321abc   md5-sessrA   N)	r	   r5   r7   r6   r9   r   r   rC   rD   )r   rG   rH   rI   ha1rJ   s         r   test_MD5SessionHashA1z%DigestAuthTests.test_MD5SessionHashA1[   s    
 
DM5$+
 
 ]T!DJ.5Ec"ggnn&&''4Z%$&43r77>>++,,*****r   c                 <    |                      dt                     dS )z
        L{calcHA1} accepts the C{'sha'} algorithm and returns a SHA hash of its
        parameters, excluding the nonce and cnonce.
           shaN)rK   r   r$   s    r   test_SHAHashA1zDigestAuthTests.test_SHAHashA1j   s     
 	FD)))))r   c                     d}t          ||| j        dd          }|dz   | j        z   }t           ||                                                    }|                     ||           dS )z
        L{calcHA2} accepts the C{'md5'} algorithm and returns an MD5 hash of
        its arguments, excluding the entity hash for QOP other than
        C{'auth-int'}.
        r4   r2   NrA   r
   r;   r   rC   rD   )r   rE   rF   r=   hashA2a2rJ   s          r   test_MD5HashA2Authz"DigestAuthTests.test_MD5HashA2Authq   sm     VTXwEEd]TX%5599++--..*****r   c                     d}d}t          ||| j        d|          }|dz   | j        z   dz   |z   }t           ||                                                    }|                     ||           dS )z
        L{calcHA2} accepts the C{'md5'} algorithm and returns an MD5 hash of
        its arguments, including the entity hash for QOP of C{'auth-int'}.
        r4   s	   foobarbazs   auth-intrA   NrT   )r   rE   rF   r=   hentityrU   rV   rJ   s           r   test_MD5HashA2AuthIntz%DigestAuthTests.test_MD5HashA2AuthInt}   s|    
 VTX{GLLd]TX%,w65599++--..*****r   c                 0    |                      d           dS )z
        L{calcHA2} accepts the C{'md5-sess'} algorithm and QOP of C{'auth'} and
        returns the same value as it does for the C{'md5'} algorithm.
        rM   N)rW   r$   s    r   test_MD5SessHashA2Authz&DigestAuthTests.test_MD5SessHashA2Auth   s    
 	,,,,,r   c                 0    |                      d           dS )z
        L{calcHA2} accepts the C{'md5-sess'} algorithm and QOP of C{'auth-int'}
        and returns the same value as it does for the C{'md5'} algorithm.
        rM   N)rZ   r$   s    r   test_MD5SessHashA2AuthIntz)DigestAuthTests.test_MD5SessHashA2AuthInt   s    
 	"";/////r   c                 <    |                      dt                     dS )z
        L{calcHA2} accepts the C{'sha'} algorithm and returns a SHA hash of
        its arguments, excluding the entity hash for QOP other than
        C{'auth-int'}.
        rQ   N)rW   r   r$   s    r   test_SHAHashA2Authz"DigestAuthTests.test_SHAHashA2Auth   s      	-----r   c                 <    |                      dt                     dS )z
        L{calcHA2} accepts the C{'sha'} algorithm and returns a SHA hash of
        its arguments, including the entity hash for QOP of C{'auth-int'}.
        rQ   N)rZ   r   r$   s    r   test_SHAHashA2AuthIntz%DigestAuthTests.test_SHAHashA2AuthInt   s     
 	""6400000r   c           	          d}d}d}|dz   |z   dz   |z   }t           ||                                                    }t          ||||ddd          }|                     ||           dS )z
        L{calcResponse} accepts the C{'md5'} algorithm and returns an MD5 hash
        of its parameters, excluding the nonce count, client nonce, and QoP
        value if the nonce count and client nonce are L{None}
           abc123   789xyz   lmnopqrA   Nr   rC   r   rD   )	r   rE   rF   rH   rU   rG   responserJ   rC   s	            r   test_MD5HashResponsez$DigestAuthTests.test_MD5HashResponse   s     D=5(4/&855??113344ffj%tTRR6*****r   c                 0    |                      d           dS )z
        L{calcResponse} accepts the C{'md5-sess'} algorithm and returns an MD5
        hash of its parameters, excluding the nonce count, client nonce, and
        QoP value if the nonce count and client nonce are L{None}
        rM   N)ri   r$   s    r   test_MD5SessionHashResponsez+DigestAuthTests.test_MD5SessionHashResponse   s     	!!+.....r   c                 <    |                      dt                     dS )z
        L{calcResponse} accepts the C{'sha'} algorithm and returns a SHA hash
        of its parameters, excluding the nonce count, client nonce, and QoP
        value if the nonce count and client nonce are L{None}
        rQ   N)ri   r   r$   s    r   test_SHAHashResponsez$DigestAuthTests.test_SHAHashResponse   s      	!!&$/////r   c           	         d}d}d}d}d}d}|dz   |z   dz   |z   dz   |z   dz   |z   dz   |z   }	t           ||	                                                    }
t          |||||||          }|                     |
|           dS )	z
        L{calcResponse} accepts the C{'md5'} algorithm and returns an MD5 hash
        of its parameters, including the nonce count, client nonce, and QoP
        value if they are specified.
        rd   re   rf   s   00000004s	   abcxyz123r2   rA   Nrg   )r   rE   rF   rH   rU   rG   
nonceCountclientNoncer:   rh   rJ   rC   s               r   test_MD5HashResponseExtraz)DigestAuthTests.test_MD5HashResponseExtra   s      
"   	
     	 
 	 55??113344FJz;
 
 	6*****r   c                 0    |                      d           dS )z
        L{calcResponse} accepts the C{'md5-sess'} algorithm and returns an MD5
        hash of its parameters, including the nonce count, client nonce, and
        QoP value if they are specified.
        rM   N)rq   r$   s    r    test_MD5SessionHashResponseExtraz0DigestAuthTests.test_MD5SessionHashResponseExtra   s     	&&{33333r   c                 <    |                      dt                     dS )z
        L{calcResponse} accepts the C{'sha'} algorithm and returns a SHA hash
        of its parameters, including the nonce count, client nonce, and QoP
        value if they are specified.
        rQ   N)rq   r   r$   s    r   test_SHAHashResponseExtraz)DigestAuthTests.test_SHAHashResponseExtra   s      	&&vt44444r   Tc                     d|vr
| j         |d<   d|vr
| j        |d<   d|vr
| j        |d<   d|vr
| j        |d<   d|vr
| j        |d<   d|vr
| j        |d<   |rdndd	                    fd
|                                D                       S )a  
        Format all given keyword arguments and their values suitably for use as
        the value of an HTTP header.

        @types quotes: C{bool}
        @param quotes: A flag indicating whether to quote the values of each
            field in the response.

        @param **kw: Keywords and C{bytes} values which will be treated as field
            name/value pairs to include in the result.

        @rtype: C{bytes}
        @return: The given fields formatted for use as an HTTP header value.
        r5   r7   r8   r:   r9   r;      "r   s   , c           	      h    g | ].\  }}|d                     t          |          d|f          /S )Nr      =)rB   r   ).0kvquotes      r   
<listcomp>z2DigestAuthTests.formatResponse.<locals>.<listcomp>  sI       Q= -**D%EBCC ==r   )r5   r7   r8   r:   r9   r;   rB   items)r   quoteskwr}   s      @r   formatResponsezDigestAuthTests.formatResponse   s     R!]BzN"*BwKb  "nB{O??BuI2;BxL??BuI 	EEEzz    hhjj  
 
 	
r   c           	      \   |                     d          }|                     d                                          }|                     d          }t          || j        | j        | j        || j                  }t          |d| j        |d          }t          |||||| j        |          }|S )z@
        Calculate the response for the given challenge
        rG   r8   r:   r4   N)
getlowerr	   r5   r7   r6   r9   r
   r;   r   )	r   	challengencountrG   algor:   rN   ha2rJ   s	            r   getDigestResponsez!DigestAuthTests.getDigestResponse  s     g&&}}[))//11mmE""$-T]E4;
 
 dFDHc488S$vt{CPPr   c                    | j                             | j        j                  }d}|                     ||d         |                     ||          ||d                   }| j                             || j        | j        j                  }|                     |	                    | j
                             |                     |	                    | j
        dz                        dS )z
        L{DigestCredentialFactory.decode} accepts a digest challenge response
        and parses it into an L{IUsernameHashedPassword} provider.
           00000001rG   opaque)r   rG   rh   ncr      wrongNr>   getChallenger<   hostr   r   decoder=   
assertTruecheckPasswordr6   assertFalse)r   r   r   r   clientResponsecredss         r   test_responsezDigestAuthTests.test_response.  s    
 *778J8OPP	,,G$++Ir::X& - 
 
 &--DK);)@
 
 	++DM::;;;,,T]X-EFFGGGGGr   c                 0    |                      d           dS )a  
        L{DigestCredentialFactory.decode} accepts a digest challenge response
        which does not quote the values of its fields and parses it into an
        L{IUsernameHashedPassword} provider in the same way it would a
        response which included quoted field values.
        FN)r   r$   s    r   test_responseWithoutQuotesz*DigestAuthTests.test_responseWithoutQuotesC  s     	5!!!!!r   c                 >    d| _         |                     d           dS )z
        L{DigestCredentialFactory.decode} accepts a digest challenge response
        which quotes the values of its fields and includes a C{b","} in the URI
        field.
        s   /some,path/TN)r;   r   r$   s    r   test_responseWithCommaURIz)DigestAuthTests.test_responseWithCommaURIL  s%     "4     r   c                 <    d| _         |                                  dS )zs
        The case of the algorithm value in the response is ignored when
        checking the credentials.
        s   MD5Nr8   r   r$   s    r   test_caseInsensitiveAlgorithmz-DigestAuthTests.test_caseInsensitiveAlgorithmU  s#    
  r   c                 <    d| _         |                                  dS )zV
        The algorithm defaults to MD5 if it is not supplied in the response.
        Nr   r$   s    r   test_md5DefaultAlgorithmz(DigestAuthTests.test_md5DefaultAlgorithm]  s#     r   c                    | j                             d          }d}|                     |d         |                     ||          ||d                   }| j                             || j        d          }|                     |                    | j                             | 	                    |                    | j        dz                        dS )z
        L{DigestCredentialFactory.decode} accepts a digest challenge response
        even if the client address it is passed is L{None}.
        Nr   rG   r   rG   rh   r   r   r   )
r>   r   r   r   r   r=   r   r   r6   r   r   r   r   r   r   s        r   test_responseWithoutClientIPz,DigestAuthTests.test_responseWithoutClientIPd  s    
 *77==	,,G$++Ir::X&	 - 
 
 &--ndk4PP++DM::;;;,,T]X-EFFGGGGGr   c                 Z   | j                             | j        j                  }d}|                     |d         |                     ||          ||d                   }| j                             || j        | j        j                  }|                     |	                    | j
                             |                     |	                    | j
        dz                        d}|                     |d         |                     ||          ||d                   }| j                             || j        | j        j                  }|                     |	                    | j
                             |                     |	                    | j
        dz                        dS )zm
        L{DigestCredentialFactory.decode} handles multiple responses to a
        single challenge.
        r   rG   r   r   r   s   00000002Nr   r   s        r   test_multiResponsez"DigestAuthTests.test_multiResponsev  s   
 *778J8OPP	,,G$++Ir::X&	 - 
 
 &--DK);)@
 
 	++DM::;;;,,T]X-EFFGGG,,G$++Ir::X&	 - 
 
 &--DK);)@
 
 	++DM::;;;,,T]X-EFFGGGGGr   c                    | j                             | j        j                  }d}|                     |d         |                     ||          ||d                   }| j                             |d| j        j                  }|                     |                    | j	                             |                     |                    | j	        dz                        dS )a&  
        L{DigestCredentialFactory.decode} returns an L{IUsernameHashedPassword}
        provider which rejects a correct password for the given user if the
        challenge response request is made using a different HTTP method than
        was used to request the initial challenge.
        r   rG   r   r   s   POSTr   N)
r>   r   r<   r   r   r   r   r   r   r6   r   s        r   test_failsWithDifferentMethodz-DigestAuthTests.test_failsWithDifferentMethod  s     *778J8OPP	,,G$++Ir::X&	 - 
 
 &--GT%7%<
 
 	,,T];;<<<,,T]X-EFFGGGGGr   c                    |                      t          | j        j        |                     d          | j        | j        j                  }|                     t          |          d           |                      t          | j        j        |                     d          | j        | j        j                  }|                     t          |          d           dS )z
        L{DigestCredentialFactory.decode} raises L{LoginFailed} if the response
        has no username field or if the username field is empty.
        N)r5   z$Invalid response, no username given.r   
assertRaisesr   r>   r   r   r=   r<   r   rD   strr   es     r   test_noUsernamezDigestAuthTests.test_noUsername  s     ")..K#
 
 	Q!GHHH ")--K#
 
 	Q!GHHHHHr   c                     |                      t          | j        j        |                     d          | j        | j        j                  }|                     t          |          d           dS )zo
        L{DigestCredentialFactory.decode} raises L{LoginFailed} if the response
        has no nonce.
        rd   )r   z!Invalid response, no nonce given.Nr   r   s     r   test_noNoncezDigestAuthTests.test_noNonce  sl    
 ")y11K#
 
 	Q!DEEEEEr   c                     |                      t          | j        j        |                                 | j        | j        j                  }|                     t          |          d           dS )zp
        L{DigestCredentialFactory.decode} raises L{LoginFailed} if the response
        has no opaque.
        z"Invalid response, no opaque given.Nr   r   s     r   test_noOpaquezDigestAuthTests.test_noOpaque  sg    
 ")!!K#
 
 	Q!EFFFFFr   c                    | j                             | j        j                  }d}|                     |d         |                     ||          ||d                   }| j                             || j        | j        j                  }|                     t          t          |                     | j        dz   | j        z   dz   | j        z   }t          |          }|                     |                    t!          |                                                               |                    d           |                     |                    t!          |                                                               dS )z
        L{DigestCredentialFactory.decode} returns an L{IUsernameDigestHash}
        provider which can verify a hash of the form 'username:realm:password'.
        r   rG   r   r   rA   r   N)r>   r   r<   r   r   r   r   r=   r   r   r   r5   r7   r6   r   	checkHashr   rC   updater   )r   r   r   r   r   	cleartexthashs          r   test_checkHashzDigestAuthTests.test_checkHash  sM   
 *778J8OPP	,,G$++Ir::X&	 - 
 
 &--DK);)@
 
 	%8%@@AAAMD(4:5<t}L	9~~(>(>??@@@H)?)?@@AAAAAr   c           	         t          | j        | j                  }|                    | j        j                  }|                     t          |j        d|d         | j        j                  }| 	                    t          |          d           dt          d          z   }|                     t          |j        ||d         | j        j                  }| 	                    t          |          d           |                     t          |j        d|d         | j        j                  }| 	                    t          |          d           dt          d                    |d         t          | j        j                  df                    z   }|                     t          |j        ||d         | j        j                  }| 	                    t          |          d	           d
S )z
        L{DigestCredentialFactory.decode} raises L{LoginFailed} when the opaque
        value does not contain all the required parts.
        s	   badOpaquerG   z&Invalid response, invalid opaque values   foo-s   nonce,clientipr      ,r0   z,Invalid response, invalid opaque/time valuesN)r   r8   r7   r   r<   r   r   r   _verifyOpaquerD   r   r   rB   r   )r   r>   r   exc	badOpaques        r   test_invalidOpaquez"DigestAuthTests.test_invalidOpaque  s   
 8
SS%2243E3JKK	+g#
 
 	S#KLLLi(9:::	+g#
 
 	S#KLLL+g#
 
 	S#KLLLiII7#]43E3J%K%KYW 
 
 
	
 +g#
 
 	S#QRRRRRr   c                    t          | j        | j                  }|                    | j        j                  }|                    d| j        j                  }|                     t          |j	        ||d         | j        j                  }| 
                    t          |          d           |                     t          |j	        |d| j        j                  }| 
                    t          |          d           dS )z
        L{DigestCredentialFactory.decode} raises L{LoginFailed} when the given
        nonce from the response does not match the nonce encoded in the opaque.
        s
   1234567890rG   z2Invalid response, incompatible opaque/nonce valuesr   N)r   r8   r7   r   r<   r   _generateOpaquer   r   r   rD   r   )r   r>   r   badNonceOpaquer   s        r   test_incompatibleNoncez&DigestAuthTests.test_incompatibleNonce1  s    
 8
SS%2243E3JKK	*::4-2
 
 +g#
 
 	S#WXXX+#
 
 	S#WXXXXXr   c                 Z   t          | j        | j                  }|                    | j        j                  }d}|                     | j        j        |           |                    |d         |          }|                     t          |j
        ||d         | j        j                   dS )z
        L{DigestCredentialFactory.decode} raises L{LoginFailed} when the
        request comes from a client IP other than what is encoded in the
        opaque.
        z10.0.0.1rG   N)r   r8   r7   r   r<   r   assertNotEqualr   r   r   r   )r   r>   r   
badAddressr   s        r   test_incompatibleClientIPz)DigestAuthTests.test_incompatibleClientIPO  s     8
SS%2243E3JKK	
D.3Z@@@*::g

 
 	+g#	
 	
 	
 	
 	
r   c                 &   t          | j        | j                  }|                    | j        j                  }d                    |d         t          | j        j                  df          }t          t          ||j
        z                                                       }t          |          }d                    ||                    d          f          }|                     t          |j        ||d         | j        j                   dS )z
        L{DigestCredentialFactory.decode} raises L{LoginFailed} when the given
        opaque is older than C{DigestCredentialFactory.CHALLENGE_LIFETIME_SECS}
        r   rG   s
   -137876876   -   
N)r   r8   r7   r   r<   r   rB   r   r   r   r   rC   r   r   r   r   r   )r   r>   r   keyrC   ekeyoldNonceOpaques          r   test_oldNoncezDigestAuthTests.test_oldNonceh  s    
 8
SS%2243E3JKK	iiwt/A/F!G!GW
 
 S#4#??@@GGIIJJ~~FDJJu,=,=#>??+g#	
 	
 	
 	
 	
r   c                    t          | j        | j                  }|                    | j        j                  }d                    |d         t          | j        j                  df          }t          t          |dz             
                                          }d                    |t          |          f          }|                     t          |j        ||d         | j        j                   dS )z~
        L{DigestCredentialFactory.decode} raises L{LoginFailed} when the opaque
        checksum fails verification.
        r   rG   r   s   this is not the right pkeyr   N)r   r8   r7   r   r<   r   rB   r   r   r   rC   r   r   r   r   )r   r>   r   r   rC   badChecksums         r   test_mismatchedOpaqueChecksumz-DigestAuthTests.test_mismatchedOpaqueChecksum  s    
 8
SS%2243E3JKK	iiwt/A/F!G!GN
 
 S#@@AAHHJJKKii3 899+g#	
 	
 	
 	
 	
r   c                 n    d}|D ]/\  }}}}|                      t          t          d|||dd|	  	         0dS )z
        L{calcHA1} raises L{TypeError} when any of the pszUsername, pszRealm,
        or pszPassword arguments are specified with the preHA1 keyword
        argument.
        ))s   user   realm   password   preHA1)Nr   Nr   )NNr   r   r1   s   nonces   cnonce)preHA1N)r   	TypeErrorr	   )r   	argumentspszUsernamepszRealmpszPasswordr   s         r   test_incompatibleCalcHA1Optionsz/DigestAuthTests.test_incompatibleCalcHA1Options  sk    
	 ;D 	 	6K;  
 
 
 
	 	r   c                 h    | j                             dd          }|                     d|           dS )z
        L{DigestCredentialFactory._generateOpaque} returns a value without
        newlines, regardless of the length of the nonce.
        sn   long nonce long nonce long nonce long nonce long nonce long nonce long nonce long nonce long nonce long nonce Nr   )r>   r   assertNotIn)r   r   s     r   test_noNewlineOpaquez$DigestAuthTests.test_noNewlineOpaque  s9    
 '778KTRR'''''r   N)T)*r(   r)   r*   r+   r?   r   rK   rO   rR   rW   rZ   r\   r^   r`   rb   ri   rk   rm   rq   rs   ru   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r#   r   r   r.   r.   8   s        U U U )/c + + + ++ + +* * * -3# 
+ 
+ 
+ 
+ 06S 
+ 
+ 
+ 
+- - -0 0 0. . .1 1 1 /5C + + + + / / /0 0 0 4: + + + +B4 4 45 5 5&
 &
 &
 &
P  H H H H*" " "! ! !    H H H$!H !H !HFH H H,I I I2F F FG G GB B B41S 1S 1SfY Y Y<
 
 
2
 
 
0
 
 
.  2( ( ( ( (r   r.   )r+   r   binasciir   hashlibr   r   zope.interface.verifyr   twisted.cred.credentialsr   r   r	   r
   r   twisted.cred.errorr   twisted.internet.addressr   twisted.python.compatr   twisted.trial.unittestr   r   r   r.   r#   r   r   <module>r      s^                   . . . . . .              + * * * * * 0 0 0 0 0 0 / / / / / / + + + + + +' ' '    "9   .~	( ~	( ~	( ~	( ~	(h ~	( ~	( ~	( ~	( ~	(r   