3
K^                 @   s  d Z ddlZddlZddlZddlZddlZddlZddlZddl	Zddl
ZddlZddlZddl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mZmZmZmZmZmZmZmZmZm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z* ej+j, rddlm-Z- nddl.m-Z- dvZ/dwZ0ej1j2j3dxdydzd{d|Z4ej5j6d-Z7ej5j8d.Z9d$Z:d/d0 Z;d}d2d3Z<d4d5 Z=d6d7 Z>d8d9 Z?d:d; Z@d<d= ZAd>d? ZBd@dA ZCdBdC ZDdDdE ZEdFdG ZFdHdI ZGeddJZHeddKZIe!ddZJe"ddZKejLeEddLdMdNZMejLeEddOdPdQZNedRdSdTdU dVZOe ddWZPe ddXZQe$ddZRe ddYZSe ddZZTedd[ZUeddd\dU dVZVeddd]dU dVZWe%d	d^d_ZXe%dd`daZYe%d
dbd_ZZe%ddcddZ[ed deZ\e%d!dfdgdhZ]ed"diZ^edjdkZ_e#dldmZ`edddndoZaG dpdq dqeZbG drds dsebZcG dtdu duebZddS )~a  
Parsing for Tor server descriptors, which contains the infrequently changing
information about a Tor relay (contact information, exit policy, public keys,
etc). This information is provided from a few sources...

* The control port via 'GETINFO desc/\*' queries.

* The 'cached-descriptors' file in Tor's data directory.

* Archived descriptors provided by `CollecTor <https://metrics.torproject.org/collector.html>`_.

* Directory authorities and mirrors via their DirPort.

**Module Overview:**

::

  ServerDescriptor - Tor server descriptor.
    |- RelayDescriptor - Server descriptor for a relay.
    |  +- make_router_status_entry - Creates a router status entry for this descriptor.
    |
    |- BridgeDescriptor - Scrubbed server descriptor for a bridge.
    |  |- is_scrubbed - checks if our content has been properly scrubbed
    |  +- get_scrubbing_issues - description of issues with our scrubbing
    |
    |- digest - calculates the upper-case hex digest value for our content
    |- get_annotations - dictionary of content prior to the descriptor entry
    +- get_annotation_lines - lines that provided the annotations

.. data:: BridgeDistribution (enum)

  Preferred method of distributing this relay if a bridge.

  .. versionadded:: 1.6.0

  ===================== ===========
  BridgeDistribution    Description
  ===================== ===========
  **ANY**               No proference, BridgeDB will pick how the bridge is distributed.
  **HTTPS**             Provided via the `web interface <https://bridges.torproject.org>`_.
  **EMAIL**             Provided in response to emails to bridges@torproject.org.
  **MOAT**              Provided in interactive menus within Tor Browser.
  **HYPHAE**            Provided via a cryptographic invitation-based system.
  ===================== ===========
    N)RouterStatusEntryV3)PGP_BLOCK_END
Descriptor
DigestHashDigestEncodingcreate_signing_key_descriptor_content_descriptor_components_read_until_keywords_bytes_for_block_value_values_parse_simple_line_parse_int_line_parse_if_present_parse_bytes_line_parse_timestamp_line_parse_forty_character_hex_parse_protocol_line_parse_key_block_append_router_signature_random_nickname_random_ipv4_address_random_date_random_crypto_blob)	lru_cacherouter	bandwidth	published	onion-keysigning-keyrouter-signatureidentity-ed25519master-key-ed25519platformfingerprinthibernatinguptimecontactread-historywrite-historyeventdnsbridge-distribution-requestfamilycaches-extra-infoextra-info-digesthidden-service-dir	protocolsallow-single-hop-exitstunnelled-dir-serverprotoonion-key-crosscertntor-onion-keyntor-onion-key-crosscertrouter-sig-ed25519ANYanyHTTPShttpsEMAILemailMOATmoatHYPHAEhyphaezreject 1-65535z
reject *:*c             C   s   t jjjtj| jdS )N   =)stemutil	str_tools_to_unicodebase64	b64encoderstrip)content rL   E/tmp/pip-unpacked-wheel-1_ii6739/stem/descriptor/server_descriptor.py_truncated_b64encode   s    rN   Fc             k   s   xt d| }ttj|}ttjjj|}tt	dd |}|sjt d| }t
jddd }|t || d7 }nt d	| d}|r|d jd
r|dd }tjd|}|rt|||f|V  qt|||f|V  q|r|rtddj| P qW dS )a8  
  Iterates over the server descriptors in a file.

  :param file descriptor_file: file with descriptor content
  :param bool is_bridge: parses the file as being a bridge descriptor
  :param bool validate: checks the validity of the descriptor's content if
    **True**, skips these checks otherwise
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: iterator for ServerDescriptor instances in the file

  :raises:
    * **ValueError** if the contents is malformed and validate is True
    * **IOError** if the file can't be read
  r   c             S   s   | dkS )N rL   )xrL   rL   rM   <lambda>   s    z_parse_file.<locals>.<lambda>zrouter-signature    r   Tzrouter-digests   @typeN    z0Content conform to being a server descriptor:
%s
)r
   mapbytesstriprD   rE   rF   rG   listfilterr   split
startswithjoinBridgeDescriptorRelayDescriptor
ValueError)Zdescriptor_fileZ	is_bridgevalidatekwargsannotationsZdescriptor_contentZblock_end_prefixZdescriptor_textrL   rL   rM   _parse_file   s(    +

rd   c             C   sP  t d|}|j }t|dk r,td| ntjjj|d sPtd|d  ntjjj	|d sttd|d  nvtjjj
|d d	d
std|d  nNtjjj
|d d	d
std|d  n&tjjj
|d d	d
std|d  |d | _|d | _t|d | _|d dkrd n
t|d | _|d dkr>d n
t|d | _d S )Nr      z,Router line must have five values: router %sr   z,Router line entry isn't a valid nickname: %srS   z0Router line entry isn't a valid IPv4 address: %s   T)Z
allow_zeroz#Router line's ORPort is invalid: %s   z&Router line's SocksPort is invalid: %s   z$Router line's DirPort is invalid: %s0)r   r[   lenr`   rD   rE   	tor_toolsZis_valid_nickname
connectionis_valid_ipv4_addressis_valid_portnicknameaddressintor_port
socks_portdir_port)
descriptorentriesvalueZrouter_comprL   rL   rM   _parse_router_line   s&    


 rx   c             C   s   t d|}|j }t|dk r,td| nX|d j sJtd|d  n:|d j shtd|d  n|d j std	|d  t|d | _t|d | _t|d | _d S )
Nr   rg   z3Bandwidth line must have three values: bandwidth %sr   z/Bandwidth line's average rate isn't numeric: %srS   z-Bandwidth line's burst rate isn't numeric: %srf   z0Bandwidth line's observed rate isn't numeric: %s)	r   r[   rj   r`   isdigitrq   average_bandwidthburst_bandwidthobserved_bandwidth)ru   rv   rw   Zbandwidth_comprL   rL   rM   _parse_bandwidth_line   s    
r}   c             C   sd   t dd| | td|}tjd|}|r`|j \}| _ytjj|| _	W n t
k
r^   Y nX d S )Nr$   z^(?:node-)?Tor (\S*).* on (.*)$)r   r   rematchgroupsoperating_systemrD   version_get_versiontor_versionr`   )ru   rv   rw   Zplatform_matchversion_strrL   rL   rM   _parse_platform_line  s    
r   c             C   sf   t d|}|jdd}x*|jdD ]}t|dkr"td| q"W tjjj|s\td| || _	d S )Nr%   rR   rO   rh   z=Fingerprint line should have groupings of four hex digits: %sz6Tor relay fingerprints consist of forty hex digits: %s)
r   replacer[   rj   r`   rD   rE   rk   Zis_valid_fingerprintr%   )ru   rv   rw   r%   groupingrL   rL   rM   _parse_fingerprint_line.  s    
r   c             C   s`   t d|}|jd}tjjj|d ds8td|d  |d | _t|dkrV|d nd | _	d S )Nzextra-info-digestrR   r   (   z1extra-info-digest should be 40 hex characters: %srf   rS   )
r   r[   rD   rE   rk   Zis_hex_digitsr`   extra_info_digestrj   extra_info_sha256_digest)ru   rv   rw   Zdigest_comprL   rL   rM   _parse_extrainfo_digest_line?  s    


r   c             C   s,   t d|}|dkrtd| |dk| _d S )Nr&   ri   1z>Hibernating line had an invalid value, must be zero or one: %s)ri   r   )r   r`   r&   )ru   rv   rw   rL   rL   rM   _parse_hibernating_lineJ  s    
r   c             C   sN   t d|}tjd|}|s&td| |j \}}|jd| _|jd| _d S )Nr1   z^Link (.*) Circuit (.*)$z?Protocols line did not match the expected pattern: protocols %srR   )r   r~   r   r`   r   r[   link_protocolscircuit_protocols)ru   rv   rw   Zprotocols_matchZlink_versionsZcircuit_versionsrL   rL   rM   _parse_protocols_lineU  s    
r   c             C   s   t d|}g }x|D ]}d| }d|kr4td| |jdd\}}tjjj| rttjjj|dd rttd| tjjj|std	| |j	|j
d
jdt|tjjj|ddf qW || _d S )Nz
or-addresszor-address %s:z#or-address line missing a colon: %srS   T)Zallow_bracketsz+or-address line has a malformed address: %sz(or-address line has a malformed port: %s[])r   r`   rsplitrD   rE   rl   rm   Zis_valid_ipv6_addressrn   appendlstriprJ   rq   or_addresses)ru   rv   Z
all_valuesr   entrylinerp   portrL   rL   rM   _parse_or_address_linea  s    

$2r   c             C   s   t | |}tjjj| |\}}}	y"|	r<dd |	jdD }
ng }
W n& tk
rh   td| | |f Y nX t||| t||| t|||
 d S )Nc             S   s   g | ]}t |qS rL   )rq   ).0r   rL   rL   rM   
<listcomp>~  s    z'_parse_history_line.<locals>.<listcomp>,z%%s line has non-numeric values: %s %s)r   rD   ru   Zextrainfo_descriptorZ_parse_timestamp_and_intervalr[   r`   setattr)keywordZhistory_end_attributeZhistory_interval_attributeZhistory_values_attributeru   rv   rw   	timestampinterval	remainderZhistory_valuesrL   rL   rM   _parse_history_linex  s    
r   c             C   sH   t | drD| jr0tjjj| jd dkr0t| _ntjj| j | _| `d S )N_unparsed_exit_policyr   z
reject *:*)	hasattrr   rD   rE   rF   rG   REJECT_ALL_POLICYexit_policy
ExitPolicy)ru   rv   rL   rL   rM   _parse_exit_policy  s
    
r   c             C   s0   t ddd| | | jr,tjjjj| j| _d S )Nzidentity-ed25519ed25519_certificatezED25519 CERT)r   r   rD   ru   certificateZEd25519CertificateZfrom_base64)ru   rv   rL   rL   rM   _parse_identity_ed25519_line  s    r   ed25519_master_keyed25519_certificate_hashread_history_endread_history_intervalread_history_valueswrite_history_endwrite_history_intervalwrite_history_valueszipv6-policyexit_policy_v6c             C   s   t jj| S )N)rD   r   MicroExitPolicy)vrL   rL   rM   rQ     s    rQ   )funcallow_single_hop_exitsallow_tunneled_dir_requestsis_hidden_service_dirextra_info_cachebridge_distributionc             C   s   t | jdS )NrR   )setr[   )r   rL   rL   rM   rQ     s    c             C   s   | dkS )Nr   rL   )r   rL   rL   rM   rQ     s    	onion_keyzRSA PUBLIC KEYonion_key_crosscertZ	CROSSCERTsigning_key	signature	SIGNATUREntor_onion_keyntor_onion_key_crosscertzED25519 CERTntor_onion_key_crosscert_signed25519_signaturezrouter-digest-sha256router_digest_sha256zrouter-digest_digestT)Zallow_negativec            '       s  e Zd ZdZdefdefdefdefdefdefdefdefdefde	fde	fde	fde
feefeefe efdefdefdefdefdefdefdefdefdefi efdefdefdefdefdefg efdefdefdefdefdefdefd&Zeee	eeeeeee
eeeeeeeeeeeeedZ d fdd	Z!e"j#e$j%fdd	Z&e' d
d Z(dd Z)dd Z*dd Z+dd Z,dd Z-dd Z.  Z/S )ServerDescriptora  
  Common parent for server descriptors.

  :var str nickname: **\*** relay's nickname
  :var str fingerprint: identity key fingerprint
  :var datetime published: **\*** time in UTC when this descriptor was made

  :var str address: **\*** IPv4 address of the relay
  :var int or_port: **\*** port used for relaying
  :var int socks_port: **\*** port used as client (**deprecated**, always **None**)
  :var int dir_port: **\*** port used for descriptor mirroring

  :var bytes platform: line with operating system and tor version
  :var stem.version.Version tor_version: version of tor
  :var str operating_system: operating system
  :var int uptime: uptime when published in seconds
  :var bytes contact: contact information
  :var stem.exit_policy.ExitPolicy exit_policy: **\*** stated exit policy
  :var stem.exit_policy.MicroExitPolicy exit_policy_v6: **\*** exit policy for IPv6
  :var BridgeDistribution bridge_distribution: **\*** preferred method of providing this relay's
    address if a bridge
  :var set family: **\*** nicknames or fingerprints of declared family

  :var int average_bandwidth: **\*** average rate it's willing to relay in bytes/s
  :var int burst_bandwidth: **\*** burst rate it's willing to relay in bytes/s
  :var int observed_bandwidth: **\*** estimated capacity based on usage in bytes/s

  :var list link_protocols: link protocols supported by the relay
  :var list circuit_protocols: circuit protocols supported by the relay
  :var bool is_hidden_service_dir: **\*** indicates if the relay serves hidden
    service descriptors
  :var bool hibernating: **\*** hibernating when published
  :var bool allow_single_hop_exits: **\*** flag if single hop exiting is allowed
  :var bool allow_tunneled_dir_requests: **\*** flag if tunneled directory
    requests are accepted
  :var bool extra_info_cache: **\*** flag if a mirror for extra-info documents
  :var str extra_info_digest: upper-case hex encoded digest of our extra-info document
  :var str extra_info_sha256_digest: base64 encoded sha256 digest of our extra-info document
  :var bool eventdns: flag for evdns backend (**deprecated**, always unset)
  :var str ntor_onion_key: base64 key used to encrypt EXTEND in the ntor protocol
  :var list or_addresses: **\*** alternative for our address/or_port
    attributes, each entry is a tuple of the form (address (**str**), port
    (**int**), is_ipv6 (**bool**))
  :var dict protocols: mapping of protocols to their supported versions

  **Deprecated**, moved to extra-info descriptor...

  :var datetime read_history_end: end of the sampling interval
  :var int read_history_interval: seconds per interval
  :var list read_history_values: bytes read during each interval

  :var datetime write_history_end: end of the sampling interval
  :var int write_history_interval: seconds per interval
  :var list write_history_values: bytes written during each interval

  **\*** attribute is either required when we're parsed with validation or has
  a default value, others are left as **None** if undefined

  .. versionchanged:: 1.5.0
     Added the allow_tunneled_dir_requests attribute.

  .. versionchanged:: 1.6.0
     Added the extra_info_sha256_digest, protocols, and bridge_distribution
     attributes.

  .. versionchanged:: 1.7.0
     Added the is_hidden_service_dir attribute.

  .. versionchanged:: 1.7.0
     Deprecated the hidden_service_dir field, it's never been populated
     (:spec:`43c2f78`). This field will be removed in Stem 2.0.
  NF)&ro   r%   r(   r   r   rp   rr   rs   rt   r$   r   r   r'   r   r   r-   rz   r{   r|   r   r   r   r&   r   r   r1   r   r   r   r+   r   r   r   r   r   r   r   r   )r   r   r$   r   r%   r(   r&   zextra-info-digestzhidden-service-dirr'   r1   zntor-onion-keyz
or-addresszread-historyzwrite-historyzipv6-policyzallow-single-hop-exitsztunnelled-dir-serverr4   zcaches-extra-infozbridge-distribution-requestr-   r+   c                s   t t| j|| d |r|ng | _ttjjj||ddd\}| _	dg| _
|r| j|| t| | |r| jr| jr| jdk r| jtjjd	krtd
| j| jf | j| n|| _dS )a  
    Server descriptor constructor, created from an individual relay's
    descriptor content (as provided by 'GETINFO desc/*', cached descriptors,
    and metrics).

    By default this validates the descriptor's content as it's parsed. This
    validation can be disables to either improve performance or be accepting of
    malformed data.

    :param str raw_contents: descriptor content provided by the relay
    :param bool validate: checks the validity of the descriptor's content if
      **True**, skips these checks otherwise
    :param list annotations: lines that appeared prior to the descriptor

    :raises: **ValueError** if the contents is malformed and validate is True
    )Z	lazy_loadacceptrejectr(   r$   )Zextra_keywordsZnon_ascii_fields2r   z0.1.2.7z;Descriptor for version '%s' had a negative uptime value: %iN)r   r   )r(   r$   )superr   __init___annotation_linesr	   rD   rE   rF   rG   r   Zhidden_service_dir_parser   r'   r   r   Versionr`   _check_constraints_entries)selfraw_contentsra   rc   rv   )	__class__rL   rM   r   V  s    
 
zServerDescriptor.__init__c             C   s   t ddS )a  
    Digest of this descriptor's content. These are referenced by...

      * **Consensus**

        * Referer: :class:`~stem.descriptor.router_status_entry.RouterStatusEntryV3` **digest** attribute
        * Format: **SHA1/BASE64**

    .. versionchanged:: 1.8.0
       Added the hash_type and encoding arguments.

    :param stem.descriptor.DigestHash hash_type: digest hashing algorithm
    :param stem.descriptor.DigestEncoding encoding: digest encoding

    :returns: **hashlib.HASH** or **str** based on our encoding argument
    zRUnsupported Operation: this should be implemented by the ServerDescriptor subclassN)NotImplementedError)r   	hash_typeencodingrL   rL   rM   digest  s    zServerDescriptor.digestc             C   sB   i }x8| j D ].}d|kr2|jdd\}}|||< qd||< qW |S )aT  
    Provides content that appeared prior to the descriptor. If this comes from
    the cached-descriptors file then this commonly contains content like...

    ::

      @downloaded-at 2012-03-18 21:18:29
      @source "173.254.216.66"

    .. deprecated:: 1.8.0
       Users very rarely read from cached descriptor files any longer. This
       method will be removed in Stem 2.x. If you have some need for us to keep
       this please `let me know
       <https://trac.torproject.org/projects/tor/wiki/doc/stem/bugs>`_.

    :returns: **dict** with the key/value pairs in our annotations
    rR   rS   N)r   r[   )r   Zannotation_dictr   keyrw   rL   rL   rM   get_annotations  s    
z ServerDescriptor.get_annotationsc             C   s   | j S )ao  
    Provides the lines of content that appeared prior to the descriptor. This
    is the same as the
    :func:`~stem.descriptor.server_descriptor.ServerDescriptor.get_annotations`
    results, but with the unparsed lines and ordering retained.

    .. deprecated:: 1.8.0
       Users very rarely read from cached descriptor files any longer. This
       method will be removed in Stem 2.x. If you have some need for us to keep
       this please `let me know
       <https://trac.torproject.org/projects/tor/wiki/doc/stem/bugs>`_.

    :returns: **list** with the lines of annotation that came before this descriptor
    )r   )r   rL   rL   rM   get_annotation_lines  s    z%ServerDescriptor.get_annotation_linesc             C   s
  x$| j  D ]}||kr
td| q
W x4| j D ](}||kr0t|| dkr0td| q0W | j }|r|t|j d krtd| | j }|r|t|j d krtd| d|j krd|j krtd	n dt|j dd krtd| jstddS )z
    Does a basic check that the entries conform to this descriptor type's
    constraints.

    :param dict entries: keyword => (value, pgp key) entries

    :raises: **ValueError** if an issue arises in validation
    z!Descriptor must have a '%s' entryrS   z3The '%s' entry can only appear once in a descriptorr   z'Descriptor must start with a '%s' entryz%Descriptor must end with a '%s' entryzidentity-ed25519zrouter-sig-ed25519zKDescriptor must have router-sig-ed25519 entry to accompany identity-ed25519rf   NzCDescriptor must have 'router-sig-ed25519' as the next-to-last entryz<Descriptor must have at least one 'accept' or 'reject' entry)	_required_fieldsr`   _single_fieldsrj   _first_keywordrY   keys_last_keywordr   )r   rv   r   Zexpected_first_keywordZexpected_last_keywordrL   rL   rM   r     s&    

z#ServerDescriptor._check_constraintsc             C   s   t S )N)REQUIRED_FIELDS)r   rL   rL   rM   r     s    z!ServerDescriptor._required_fieldsc             C   s   t t S )N)r   SINGLE_FIELDS)r   rL   rL   rM   r     s    zServerDescriptor._single_fieldsc             C   s   dS )Nr   rL   )r   rL   rL   rM   r     s    zServerDescriptor._first_keywordc             C   s   dS )Nzrouter-signaturerL   )r   rL   rL   rM   r     s    zServerDescriptor._last_keyword)FN)0__name__
__module____qualname____doc__rx   r   _parse_contact_line_parse_published_liner   r   _parse_uptime_lineDEFAULT_IPV6_EXIT_POLICY_parse_ipv6_policy_lineDEFAULT_BRIDGE_DISTRIBUTION'_parse_bridge_distribution_request_liner   _parse_family_liner}   r   _parse_hidden_service_dir_liner   "_parse_allow_single_hop_exits_line_parse_tunneled_dir_server_line_parse_proto_line_parse_caches_extra_info_liner   _parse_eventdns_line_parse_ntor_onion_key_liner   _parse_read_history_line_parse_write_history_line
ATTRIBUTESPARSER_FOR_LINEr   r   SHA1r   HEXr   r   r   r   r   r   r   r   r   __classcell__rL   rL   )r   rM   r     s   H5&r   c                   s   e Zd ZdZdZeejfdefdefde	fde
fdefdefdefdefdefdefd
Zeejfee	e
eeeeedZd fdd	Zedf dddfd	d
Zedf ddddfddZe ejejfddZdd Ze dd Z fddZ  ZS )r_   a  
  Server descriptor (`descriptor specification
  <https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt>`_)

  :var stem.certificate.Ed25519Certificate certificate: ed25519 certificate
  :var str ed25519_certificate: base64 encoded ed25519 certificate
  :var str ed25519_master_key: base64 encoded master key for our ed25519 certificate
  :var str ed25519_signature: signature of this document using ed25519

  :var str onion_key: **\*** key used to encrypt EXTEND cells
  :var str onion_key_crosscert: signature generated using the onion_key
  :var str ntor_onion_key_crosscert: signature generated using the ntor-onion-key
  :var str ntor_onion_key_crosscert_sign: sign of the corresponding ed25519 public key
  :var str signing_key: **\*** relay's long-term identity key
  :var str signature: **\*** signature for this descriptor

  **\*** attribute is required when we're parsed with validation

  .. versionchanged:: 1.5.0
     Added the ed25519_certificate, ed25519_master_key, ed25519_signature,
     onion_key_crosscert, ntor_onion_key_crosscert, and
     ntor_onion_key_crosscert_sign attributes.

  .. versionchanged:: 1.6.0
     Moved from the deprecated `pycrypto
     <https://www.dlitz.net/software/pycrypto/>`_ module to `cryptography
     <https://pypi.org/project/cryptography/>`_ for validating signatures.

  .. versionchanged:: 1.6.0
     Added the certificate attribute.

  .. deprecated:: 1.6.0
     Our **ed25519_certificate** is deprecated in favor of our new
     **certificate** attribute. The base64 encoded certificate is available via
     the certificate's **encoded** attribute.

  .. versionchanged:: 1.6.0
     Added the **skip_crypto_validation** constructor argument.
  zserver-descriptorN)
r   r   r   r   r   r   r   r   r   r   )zidentity-ed25519zmaster-key-ed25519zrouter-sig-ed25519z	onion-keyzonion-key-crosscertzntor-onion-key-crosscertzsigning-keyzrouter-signatureFc                s   t t| j||| |r| jrVtjt| jj }|| jj	 krVt
d| jj	 |f | rtjj r| j| j| j}|| j krt
d|| j f | jrtjj r| j| j| j}|| j krt
d|| j f tjjddr| jr| jj|  d S )Nz^Fingerprint does not match the hash of our signing key (fingerprint: %s, signing key hash: %s)zHDecrypted digest does not match local digest (calculated: %s, local: %s)z\Decrypted onion-key-crosscert digest does not match local digest (calculated: %s, local: %s)T)Zed25519)r   r_   r   r%   hashlibsha1r   r   	hexdigestlowerr`   rD   prereqZis_crypto_availableZ_digest_for_signaturer   r   r   r   _onion_key_crosscert_digestr   ra   )r   r   ra   rc   skip_crypto_validationZkey_hashZsigned_digestZonion_key_crosscert_digest)r   rL   rM   r   G  s     zRelayDescriptor.__init__c       	      C   s8  |rd}|d kri }|d kr t }ddt t f fdt fdgdd t|j D  d	td
fdtd
fg }|r|rd|krtdn|rd|krtd|d krt }d|krt	j
ttjjj|jj j j }djtjjj|d|d< |j|d< t|||d }t||jS t|||ddtdffS d S )NTr   z%s %s 9001 0 0r   r   153600 256000 104590c             S   s   g | ]}t |jd dqS )rR   rS   )tupler[   )r   r   rL   rL   rM   r   p  s    z+RelayDescriptor.content.<locals>.<listcomp>z	onion-keyzRSA PUBLIC KEYzsigning-keyz=Cannot sign the descriptor if a signing-key has been providedzrouter-signaturezBCannot sign the descriptor if a router-signature has been providedr%   rR   rh   s   
router-signature
router-sig-ed25519r   )r   r  )r  N)r   r   r   r   str
splitlinesr   r`   r   r  r  r   rD   rE   rF   rG   Zpublic_digestrX   r  upperr]   Z_split_by_lengthr   r   private)	clsattrexcludesignr   r   Zbase_headerr%   rK   rL   rL   rM   rK   `  s2    4

&
zRelayDescriptor.contentTc             C   s   | | j ||||||| dS )N)ra   r  )rK   )r  r  r  ra   r  r   r   rL   rL   rM   create  s    zRelayDescriptor.createc             C   sZ   | j ddd}|tjkr,tjjtj||S |tjkrJtjjtj	||S t
d| dS )z
    Provides the digest of our descriptor's content.

    :returns: the digest string encoded in uppercase hex

    :raises: ValueError if the digest cannot be calculated
    r   z
router-signature
)startendzGServer descriptor digests are only available in sha1 and sha256, not %sN)Z_content_ranger   r  rD   ru   Z_encode_digestr  r  SHA256sha256r   )r   r   r   rK   rL   rL   rM   r     s    


zRelayDescriptor.digestc          	   C   s   | j stddj| jttjtjj	j
| j ttjtjj	j
| j | jjd| jt| j| jrnt| jndgd| j | jj jddd}| jrd	| j |d
< | jrdd | jD |d< | jrdt| jj |d< tj|S )z
    Provides a RouterStatusEntryV3 for this descriptor content.

    .. versionadded:: 1.6.0

    :returns: :class:`~stem.descriptor.router_status_entry.RouterStatusEntryV3`
      that would be in the consensus
    zmServer descriptor lacks a fingerprint. This is an optional field, but required to make a router status entry.rR   z%Y-%m-%d %H:%M:%Sri   zBandwidth=%iz, r   )rwpzTor %sr   c             S   s   g | ]\}}}d ||f qS )z%s:%srL   )r   addrr   _rL   rL   rM   r     s    z<RelayDescriptor.make_router_status_entry.<locals>.<listcomp>az
ed25519 %sid)r%   r`   r]   ro   rN   binascii	unhexlifyrD   rE   rF   	_to_bytesr   r   strftimerp   r  rr   rt   rz   r   summaryr   r   r   r   r   r   r  )r   r  rL   rL   rM   make_router_status_entry  s&    

z(RelayDescriptor.make_router_status_entryc             C   sJ   t jt| jj }|tjtjj	j
| jd  }tjj	jtj|j S )z
    Provides the digest of the onion-key-crosscert data. This consists of the
    RSA identity key sha1 and ed25519 identity key.

    :returns: **unicode** digest encoded in uppercase hex

    :raises: ValueError if the digest cannot be calculated
    rC   )r  r  r   r   r   rH   	b64decoderD   rE   rF   r%  r   rG   r#  hexlifyr  )r   Zsigning_key_digestdatarL   rL   rM   r
    s    z+RelayDescriptor._onion_key_crosscert_digestc                s8   t t| j| | jr4| js&tdn| js4tdd S )NzMDescriptor must have a 'onion-key-crosscert' when identity-ed25519 is presentzLDescriptor must have a 'router-sig-ed25519' when identity-ed25519 is present)r   r_   r   r   r   r`   r   )r   rv   )r   rL   rM   r     s    
z"RelayDescriptor._check_constraints)FNF)r   r   r   r   TYPE_ANNOTATION_NAMEdictr   r   r   _parse_master_key_ed25519_line_parse_router_sig_ed25519_line_parse_onion_key_line_parse_onion_key_crosscert_line$_parse_ntor_onion_key_crosscert_line_parse_signing_key_line_parse_router_signature_liner  r   classmethodrK   r  r   r   r  r   r  r   r(  r
  r   r  rL   rL   )r   rM   r_     s@   '
,&r_   c               @   s   e Zd ZdZdZeejfdefde	fde
fdZeejfee	e
dZedf dfddZejejfd	d
Zdd Ze dd Zdd Zdd Zdd ZdS )r^   a  
  Bridge descriptor (`bridge descriptor specification
  <https://metrics.torproject.org/collector.html#bridge-descriptors>`_)

  :var str ed25519_certificate_hash: sha256 hash of the original identity-ed25519
  :var str router_digest_sha256: sha256 digest of this document

  .. versionchanged:: 1.5.0
     Added the ed25519_certificate_hash and router_digest_sha256 attributes.
     Also added ntor_onion_key (previously this only belonged to unsanitized
     descriptors).
  zbridge-server-descriptorN)r   r   r   )zmaster-key-ed25519zrouter-digest-sha256zrouter-digestFc          	   C   s>   |rt d| j t||ddt t f fddt fddfS )NzSigning of %s not implementedr   z%s %s 9001 0 0router-digest(006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4r   r   409600 819200 5120r   *:*)r6  r7  )r   r8  )r   r9  )r   r   r   r   r   r   )r  r  r  r  rL   rL   rM   rK     s    zBridgeDescriptor.contentc             C   s.   |t jkr|tjkr| jS td||f d S )NzJBridge server descriptor digests are only available as sha1/hex, not %s/%s)r   r  r   r  r   r   )r   r   r   rL   rL   rM   r     s    zBridgeDescriptor.digestc             C   s   | j  g kS )a<  
    Checks if we've been properly scrubbed in accordance with the `bridge
    descriptor specification
    <https://metrics.torproject.org/collector.html#bridge-descriptors>`_.
    Validation is a moving target so this may not be fully up to date.

    :returns: **True** if we're scrubbed, **False** otherwise
    )get_scrubbing_issues)r   rL   rL   rM   is_scrubbed  s    
zBridgeDescriptor.is_scrubbedc             C   s   g }| j jds |jd| j   | jr@| jdkr@|jd| j  xT| jD ]J\}}}| rt|jd rt|jd|  qH|rH|jd rH|jd|  qHW x\| j D ]P}|jdr|jd	|  q|jd
r|jd|  q|jdr|jd|  qW |S )z
    Provides issues with our scrubbing.

    :returns: **list** of strings which describe issues we have with our
      scrubbing, this list is empty if we're properly scrubbed
    z10.z=Router line's address should be scrubbed to be '10.x.x.x': %sZsomebodyzFContact line should be scrubbed to be 'somebody', but instead had '%s'zAor-address line's address should be scrubbed to be '10.x.x.x': %szfd9f:2e19:3bcf::zPor-address line's address should be scrubbed to be 'fd9f:2e19:3bcf::xx:xxxx': %sz
onion-key z;Bridge descriptors should have their onion-key scrubbed: %szsigning-key z=Bridge descriptors should have their signing-key scrubbed: %szrouter-signature z;Bridge descriptors should have their signature scrubbed: %s)rp   r\   r   r(   r   Zget_unrecognized_lines)r   Zissuesrp   r   is_ipv6r   rL   rL   rM   r:    s$    	


z%BridgeDescriptor.get_scrubbing_issuesc                s*   dddg dg}t | fddtD  S )Nz	onion-keyzsigning-keyzrouter-signaturezrouter-digestc                s   g | ]}| kr|qS rL   rL   )r   f)excluded_fieldsrL   rM   r   O  s    z5BridgeDescriptor._required_fields.<locals>.<listcomp>)r  r   )r   Zincluded_fieldsrL   )r>  rM   r   A  s
    z!BridgeDescriptor._required_fieldsc             C   s   | j  t S )N)r   r   )r   rL   rL   rM   r   Q  s    zBridgeDescriptor._single_fieldsc             C   s   d S )NrL   )r   rL   rL   rM   r   T  s    zBridgeDescriptor._last_keyword)r   r   r   r   r,  r-  r   r   '_parse_master_key_ed25519_for_hash_line _parse_router_digest_sha256_line_parse_router_digest_liner  r5  rK   r   r  r   r  r   r;  r   r:  r   r   r   rL   rL   rL   rM   r^     s$   
#r^   )r   r   r   r   r    r!   )r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   r7   r8   )r9   r:   )r;   r<   )r=   r>   )r?   r@   )rA   rB   )FF)er   rH   r#  	functoolsr  r~   Zstem.descriptor.certificaterD   Z$stem.descriptor.extrainfo_descriptorZstem.exit_policyZstem.prereqZstem.util.connectionZstem.util.enumZstem.util.str_toolsZstem.util.tor_toolsZstem.versionZ#stem.descriptor.router_status_entryr   Zstem.descriptorr   r   r   r   r   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r	  Z_is_lru_cache_availabler   Zstem.util.lru_cacher   r   rE   enumEnumZBridgeDistributionr   r   r   r   r   r   rN   rd   rx   r}   r   r   r   r   r   r   r   r   r   r.  r?  r   r   partialr   r   r   r   r   r   r   r   r   r   r   r0  r1  r3  r4  r   r2  r/  r@  rA  r   r   r_   r^   rL   rL   rL   rM   <module>0   s   h                           
L
	













  B a