o
    ]                     @   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+, rddlm-Z- nddl.m-Z- dZ/dZ0ej1j23dd	d
ddZ4ej56dZ7ej58dZ9dZ:dd Z;dmddZ<dd Z=dd Z>dd Z?dd Z@dd ZAdd  ZBd!d" ZCd#d$ ZDd%d& ZEd'd( ZFd)d* ZGed+d,ZHed+d-ZIe!d.d.ZJe"d/d/ZKeLeEd0d1d2d3ZMeLeEd4d5d6d7ZNed8d9d:d; d<ZOe d=d>ZPe d?d@ZQe$dAdBZRe dCdDZSe dEdFZTedGdHZUedIdIdJd; d<ZVedKdKdLd; d<ZWe%dMdNdOZXe%dPdQdRZYe%dSdTdOZZe%dUdVdWZ[edXdYZ\e%dZd[d\d]Z]ed^d_Z^ed`daZ_e#dbdcZ`edddddedfZaG dgdh dheZbG didj djebZcG dkdl dlebZddS )na  
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)ZHTTPSZhttps)ZEMAILZemail)ZMOATZmoat)ZHYPHAEZhyphaezreject 1-65535
reject *:*r:   c                 C   s   t jjt| dS )N   =)stemutil	str_tools_to_unicodebase64Z	b64encoderstripcontent rE   C/usr/lib/python3/dist-packages/stem/descriptor/server_descriptor.py_truncated_b64encode   s   rG   Fc                 k   s    	 t d| }ttj|}ttjjj|}tt	dd |}|s6t d| }t
ddd }|t || d7 }nt d	| d}|rk|d d
rK|dd }td|}|r_t|||fi |V  nt|||fi |V  n|rx|rxtdd| dS q)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
  Tr   c                 S   s   | dkS )N rE   )xrE   rE   rF   <lambda>       z_parse_file.<locals>.<lambda>r!       r   router-digests   @typeN    z0Content conform to being a server descriptor:
%s
)r
   mapbytesstripr=   r>   r?   r@   listfilterr   split
startswithjoinBridgeDescriptorRelayDescriptor
ValueError)Zdescriptor_fileZ	is_bridgevalidatekwargsannotationsZdescriptor_contentZblock_end_prefixZdescriptor_textrE   rE   rF   _parse_file   s,   +

r_   c                 C   sH  t d|}| }t|dk rtd| tjj|d s&td|d  tjj	|d s7td|d  tjjj
|d d	d
sJtd|d  tjjj
|d d	d
s]td|d  tjjj
|d d	d
sptd|d  |d | _|d | _t|d | _|d dkrd nt|d | _|d dkrd | _d S t|d | _d S )Nr      z,Router line must have five values: router %sr   z,Router line entry isn't a valid nickname: %srM   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   rV   lenr[   r=   r>   	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_comprE   rE   rF   _parse_router_line   s&   


(rs   c                 C   s   t d|}| }t|dk rtd| |d  s#td|d  |d  s1td|d  |d  s?td	|d  t|d | _t|d | _t|d | _d S )
Nr   rb   z3Bandwidth line must have three values: bandwidth %sr   z/Bandwidth line's average rate isn't numeric: %srM   z-Bandwidth line's burst rate isn't numeric: %sra   z0Bandwidth line's observed rate isn't numeric: %s)	r   rV   re   r[   isdigitrl   average_bandwidthburst_bandwidthobserved_bandwidth)rp   rq   rr   Zbandwidth_comprE   rE   rF   _parse_bandwidth_line   s   
rx   c                 C   sf   t dd| | td|}td|}|r1| \}| _z
tj|| _	W d S  t
y0   Y d S w d S )Nr$   z^(?:node-)?Tor (\S*).* on (.*)$)r   r   rematchgroupsoperating_systemr=   versionZ_get_versiontor_versionr[   )rp   rq   rr   Zplatform_matchZversion_strrE   rE   rF   _parse_platform_line  s   
r   c                 C   sb   t d|}|dd}|dD ]}t|dkrtd| qtjj|s,td| || _	d S )Nr%   rL   rH   rc   z=Fingerprint line should have groupings of four hex digits: %sz6Tor relay fingerprints consist of forty hex digits: %s)
r   replacerV   re   r[   r=   r>   rf   Zis_valid_fingerprintr%   )rp   rq   rr   r%   groupingrE   rE   rF   _parse_fingerprint_line.  s   

r   c                 C   sf   t d|}|d}tjj|d dstd|d  |d | _t|dkr.|d | _	d S d | _	d S )Nr/   rL   r   (   z1extra-info-digest should be 40 hex characters: %sra   rM   )
r   rV   r=   r>   rf   Zis_hex_digitsr[   extra_info_digestre   extra_info_sha256_digest)rp   rq   rr   Zdigest_comprE   rE   rF   _parse_extrainfo_digest_line?  s   


$r   c                 C   s,   t d|}|dvrtd| |dk| _d S )Nr&   )rd   1z>Hibernating line had an invalid value, must be zero or one: %sr   )r   r[   r&   )rp   rq   rr   rE   rE   rF   _parse_hibernating_lineJ  s   
r   c                 C   sN   t d|}td|}|std| | \}}|d| _|d| _d S )Nr1   z^Link (.*) Circuit (.*)$z?Protocols line did not match the expected pattern: protocols %srL   )r   ry   rz   r[   r{   rV   link_protocolscircuit_protocols)rp   rq   rr   Zprotocols_matchZlink_versionsZcircuit_versionsrE   rE   rF   _parse_protocols_lineU  s   
r   c              	   C   s   t d|}g }|D ]R}d| }d|vrtd| |dd\}}tjj|s7tjjj|dds7td| tjj|sDtd	| |	|
d
dt|tjjj|ddf q	|| _d S )N
or-addresszor-address %s:z#or-address line missing a colon: %srM   T)Zallow_bracketsz+or-address line has a malformed address: %sz(or-address line has a malformed port: %s[])r   r[   rsplitr=   r>   rg   rh   Zis_valid_ipv6_addressri   appendlstriprB   rl   or_addresses)rp   rq   Z
all_valuesr   entrylinerk   portrE   rE   rF   _parse_or_address_linea  s   
 0
r   c                 C   s   t | |}tjj| |\}}}	z|	rdd |	dD }
ng }
W n ty1   td| | |f w t||| t||| t|||
 d S )Nc                 S   s   g | ]}t |qS rE   )rl   ).0r   rE   rE   rF   
<listcomp>~  s    z'_parse_history_line.<locals>.<listcomp>,z%%s line has non-numeric values: %s %s)r   r=   rp   Zextrainfo_descriptorZ_parse_timestamp_and_intervalrV   r[   setattr)keywordZhistory_end_attributeZhistory_interval_attributeZhistory_values_attributerp   rq   rr   Z	timestampintervalZ	remainderZhistory_valuesrE   rE   rF   _parse_history_linex  s   
r   c                 C   sL   t | dr$| jrtjj| jd dkrt| _ntjj| j | _| `d S d S )N_unparsed_exit_policyr   r;   )	hasattrr   r=   r>   r?   r@   REJECT_ALL_POLICYexit_policy
ExitPolicyrp   rq   rE   rE   rF   _parse_exit_policy  s   
r   c                 C   s4   t ddd| | | jrtjjj| j| _d S d S )Nr"   ed25519_certificateED25519 CERT)r   r   r=   rp   certificateZEd25519CertificateZfrom_base64r   rE   rE   rF   _parse_identity_ed25519_line  s   r   r#   ed25519_master_keyed25519_certificate_hashr(   r   r)   read_history_endread_history_intervalread_history_valuesr*   write_history_endwrite_history_intervalwrite_history_valuesipv6-policyexit_policy_v6c                 C   s   t j| S N)r=   r   MicroExitPolicyvrE   rE   rF   rJ     s    rJ   )funcr2   allow_single_hop_exitsr3   allow_tunneled_dir_requestsr4   r1   r0   is_hidden_service_dirr.   extra_info_cacher,   bridge_distributionr-   c                 C   s   t | dS )NrL   )setrV   r   rE   rE   rF   rJ     s    r+   c                 C   s   | dkS )Nr   rE   r   rE   rE   rF   rJ     rK   r   	onion_keyRSA PUBLIC KEYr5   onion_key_crosscertZ	CROSSCERTr    signing_keyr!   	signature	SIGNATUREr6   ntor_onion_keyr7   ntor_onion_key_crosscertr   ntor_onion_key_crosscert_signr8   ed25519_signaturerouter-digest-sha256router_digest_sha256rN   _digestr'   T)Zallow_negativec                       sj  e Zd ZdZi ddefddefddefddefddefddefd	defd
defddefdde	fdde	fdde	fdde
fdeefdeefde efddefi ddefddefddefddefddefddefddefddefdi efddefddefd defd!defd"defd#g efd$defd%defdefdefdefdefd&Zi d'ed(ede	dedededed)ed*ede
ded+ed,ed-ed.ed/ed0eeeeeeed1Z dD fd2d3	Z!e"j#e$j%fd4d5Z&e' d6d7 Z(d8d9 Z)d:d; Z*d<d= Z+d>d? Z,d@dA Z-dBdC Z.  Z/S )E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.
  rj   Nr%   r(   r   r   rk   rm   rn   ro   r$   r~   r|   r'   r   r   r-   ru   rv   rw   r   r   r   Fr&   r   r   r1   r   r   r   r+   r   r   r   r   )r   r   r   r   r   r   r/   r0   r6   r   r)   r*   r   r2   )r3   r4   r.   r,   r-   r+   c                    s   t t| j|| d |r|ng | _ttjj||ddd\}| _	dg| _
|rZ| || t| | |rS| jrS| jrS| jdk rS| jtjdkrStd| j| jf | | d	S || _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)Zacceptreject)r(   r$   )Zextra_keywordsZnon_ascii_fields2r   z0.1.2.7z;Descriptor for version '%s' had a negative uptime value: %iN)superr   __init___annotation_linesr	   r=   r>   r?   r@   r   Zhidden_service_dir_parser   r'   r~   r}   ZVersionr[   _check_constraintsZ_entries)selfraw_contentsr\   r^   rq   	__class__rE   rF   r   V  s    


zServerDescriptor.__init__c                 C   s   t d)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 subclass)NotImplementedErrorr   	hash_typeencodingrE   rE   rF   digest  s   zServerDescriptor.digestc                 C   s>   i }| j D ]}d|v r|dd\}}|||< qd||< q|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
    rL   rM   N)r   rV   )r   Zannotation_dictr   keyrr   rE   rE   rF   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   rE   rE   rF   get_annotation_lines  s   z%ServerDescriptor.get_annotation_linesc                 C   s   |   D ]}||vrtd| q|  D ]}||v r)t|| dkr)td| q|  }|r@|t| d kr@td| |  }|rV|t| d krVtd| d| v rvd	| vrftd
d	t| dd vrvt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' entryrM   z3The '%s' entry can only appear once in a descriptorr   z'Descriptor must start with a '%s' entryz%Descriptor must end with a '%s' entryr"   r8   zKDescriptor must have router-sig-ed25519 entry to accompany identity-ed25519NzCDescriptor must have 'router-sig-ed25519' as the next-to-last entryz<Descriptor must have at least one 'accept' or 'reject' entry)	_required_fieldsr[   _single_fieldsre   _first_keywordrT   keys_last_keywordr   )r   rq   r   Zexpected_first_keywordZexpected_last_keywordrE   rE   rF   r     s,   
z#ServerDescriptor._check_constraintsc                 C   s   t S r   )REQUIRED_FIELDSr   rE   rE   rF   r        z!ServerDescriptor._required_fieldsc                 C   s   t t S r   )r   SINGLE_FIELDSr   rE   rE   rF   r     s   zServerDescriptor._single_fieldsc                 C      dS )Nr   rE   r   rE   rE   rF   r     r   zServerDescriptor._first_keywordc                 C   r   )Nr!   rE   r   rE   rE   rF   r     r   zServerDescriptor._last_keyword)FN)0__name__
__module____qualname____doc__rs   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_linerx   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__rE   rE   r   rF   r     s    I	

 !"#$&'(/	
5
&r   c                       s   e Zd ZdZdZeejfi 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i ee	e
eeeeedZd fdd	Zedd
dZed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 )rZ   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   )r"   r#   r8   r   r5   r7   r    r!   Fc                    s  t t| ||| |r|| jr+tt| j }|| j	 kr+t
d| j	 |f |sjtj rj| | j| j}||  krJt
d||  f | jrjtj rj| | j| j}||  krjt
d||  f tjjddr~| jr| j|  d S d S d S 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   rZ   r   r%   hashlibsha1r   r   	hexdigestlowerr[   r=   prereqZis_crypto_availableZ_digest_for_signaturer   r   r   r   _onion_key_crosscert_digestr   r\   )r   r   r\   r^   skip_crypto_validationZkey_hashZsigned_digestZonion_key_crosscert_digestr   rE   rF   r   G  s$   zRelayDescriptor.__init__rE   c           	      C   s0  |rd}|d u r
i }|d u rt }ddt t f fdt fdgdd t| D  dtd	fd
td	fg }|r|rCd
|v rCtd|rMd|v rMtd|d u rTt }d|vrxt	
ttjj|j   }dtjj|d|d< |j|d
< t|||d }t||jS t|||ddtdffS )NTr   %s %s 9001 0 0r   )r   z153600 256000 104590c                 S   s   g | ]
}t |d dqS )rL   rM   )tuplerV   )r   r   rE   rE   rF   r   o  s    z+RelayDescriptor.content.<locals>.<listcomp>r   r   r    z=Cannot sign the descriptor if a signing-key has been providedr!   zBCannot sign the descriptor if a router-signature has been providedr%   rL   rc   s   
router-signature
)r8   Nr   )r   r   r   r   str
splitlinesr   r[   r   r  r  r   r=   r>   r?   r@   Zpublic_digestrS   r  upperrX   Z_split_by_lengthr   r   Zprivate)	clsattrexcludesignr   r   Zbase_headerr%   rD   rE   rE   rF   rD   `  sF   


&

zRelayDescriptor.contentTc                 C   s   | |  ||||||| dS )N)r\   r  rC   )r  r  r  r\   r  r   r   rE   rE   rF   create  s   zRelayDescriptor.createc                 C   sV   | j ddd}|tjkrtjt||S |tjkr%tjt	||S t
d| )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 %s)Z_content_ranger   r  r=   rp   Z_encode_digestr  r  ZSHA256Zsha256r   )r   r   r   rD   rE   rE   rF   r     s   


zRelayDescriptor.digestc                 C   s   | j stdd| jtttjj	
| j tttjj	
|  | jd| jt| j| jr7t| jndgd| j | j ddd}| jrSd	| j |d
< | jr`dd | jD |d< | jrmdt| jj |d< t|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.rL   z%Y-%m-%d %H:%M:%Srd   zBandwidth=%iz, r   )rwpzTor %sr   c                 S   s   g | ]\}}}d ||f qS )z%s:%srE   )r   Zaddrr   _rE   rE   rF   r     s    z<RelayDescriptor.make_router_status_entry.<locals>.<listcomp>az
ed25519 %sid)r%   r[   rX   rj   rG   binasciiZ	unhexlifyr=   r>   r?   	_to_bytesr   r   strftimerk   r  rm   ro   ru   r   Zsummaryr   r~   r   r   r   r   r  )r   r  rE   rE   rF   make_router_status_entry  s*   

	
z(RelayDescriptor.make_router_status_entryc                 C   sJ   t t| j }|ttjj	
| jd  }tjj	t| 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
    r<   )r  r  r   r   r   rA   Z	b64decoder=   r>   r?   r   r   r@   r  Zhexlifyr  )r   Zsigning_key_digestdatarE   rE   rF   r    s   z+RelayDescriptor._onion_key_crosscert_digestc                    s:   t t| | | jr| jstd| jstdd S 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   rZ   r   r   r   r[   r   )r   rq   r   rE   rF   r     s   z"RelayDescriptor._check_constraints)FNF)NrE   FNN)NrE   TFNN)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   classmethodrD   r  r   r   r  r   r  r   r"  r  r   r  rE   rE   r   rF   rZ     sH    (



,&
rZ   c                   @   s   e Zd ZdZdZeejfi defde	fde
fdZeejfi ee	e
dZed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 )rY   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   )r#   r   rN   rE   Fc                 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   r  )rN   Z(006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4r   )r   z409600 819200 5120)r   z*:*)r   r   r   r   r   r   )r  r  r  r  rE   rE   rF   rD     s   zBridgeDescriptor.contentc                 C   s*   |t jkr|tjkr| jS td||f )NzJBridge server descriptor digests are only available as sha1/hex, not %s/%s)r   r  r   r  r   r   r   rE   rE   rF   r     s   zBridgeDescriptor.digestc                 C   s   |   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_issuesr   rE   rE   rF   is_scrubbed  s   
zBridgeDescriptor.is_scrubbedc                 C   s   g }| j ds|d| j   | jr | jdkr |d| j  | jD ]"\}}}|s7|ds7|d|  q#|rE|dsE|d|  q#|  D ](}|drY|d	|  qJ|d
rf|d|  qJ|drr|d|  qJ|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)rk   rW   r   r(   r   Zget_unrecognized_lines)r   Zissuesrk   r  Zis_ipv6r   rE   rE   rF   r.    s(   	


z%BridgeDescriptor.get_scrubbing_issuesc                    s(   g d dg}t | fddtD  S )N)r   r    r!   rN   c                    s   g | ]}| vr|qS rE   rE   )r   fZexcluded_fieldsrE   rF   r   O  s    z5BridgeDescriptor._required_fields.<locals>.<listcomp>)r  r   )r   Zincluded_fieldsrE   r1  rF   r   A  s   z!BridgeDescriptor._required_fieldsc                 C   s   |   t S r   )r   r   r   rE   rE   rF   r   Q  s   zBridgeDescriptor._single_fieldsc                 C   s   d S r   rE   r   rE   rE   rF   r   T  r   zBridgeDescriptor._last_keyword)NrE   F)r   r   r   r   r$  r%  r   r  '_parse_master_key_ed25519_for_hash_line _parse_router_digest_sha256_line_parse_router_digest_liner  r-  rD   r   r  r   r  r   r/  r   r.  r   r   r   rE   rE   rE   rF   rY     s,    




"rY   )FF)er   rA   r  	functoolsr  ry   Zstem.descriptor.certificater=   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   r>   enumEnumZBridgeDistributionr   r   r   r   r   r   rG   r_   rs   rx   r   r   r   r   r   r   r   r   r   r&  r2  r   r   partialr   r   r   r   r   r   r   r   r   r   r   r(  r)  r+  r,  r   r*  r'  r3  r4  r   r   rZ   rY   rE   rE   rE   rF   <module>   s   .h


L

	












  B a