o
    ^9                 	   @   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
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! ddlm"Z"m#Z#m$Z$m%Z% dZ&dZ'dZ(dZ)d	Z*d
Z+dZ,i dddddddddddddddddddddd d!d"d#d$d%d&d'd&d(d&d)d*d*d+d,d-d.d/dd0Z-d1Z.d2Z/d3\Z0Z1i d4d5d6d7e1fd8d7e1fd9d7e1fd:d;dd&e1fdd;dd<dd=dd>dd?dd@de0dAfdd&e1fddBe1fd!dCd#dDi d%d;d'd;d(d;dEd;dFdGe1fdHdIdJdKdLdMdNdOdPd;dQd&e1fd)dRdSdRdTdUdVdUdWdKdXdYdZd;d[Z2G d\d] d]e3d]g d^Z4G d_d` d`e3d`g daZ5G dbdc dce3dcg ddZ6ddedeej7fdfdgZ8ddhdiZ9ddjdkZ:G dldm dmeZ;dndo Z<dpdq Z=drds Z>dtdu Z?e<dvdwd-Z@edxdxZAedydyZBedzd{d|ZCed}d~dd dZDedddd dZEeddZFedddd dZGedddddZHeddZIG dd de;ZJdd ZKdd ZLdd ZMdd ZNdd ZOdd ZPdd ZQdd ZRdd ZSdd ZTdd ZUdd ZVdd ZWdd ZXdd ZYdd ZZeddZ[eddZ\eddZ]ePd}d~Z^ePddZ_edddd dZ`edddd dZaeddZbeddZceddZdeddăZeeddƃZfG ddȄ de;Zgddʄ Zhdd̄ Zidd΄ ZjeddЃZkedd҃ZlG ddԄ deZmddք Zne<ddwdZoeddZpeddڃZqeddd|Zredzd{d|Zseddd߃ZtedddZuG dd deZvG dd dewZxG dd deZyG dd de;ZzdS )aC  
Parsing for Tor network status documents. This supports both the v2 and v3
`dir-spec <https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt>`_.
Documents can be obtained from a few sources...

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

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

* Directory authorities and mirrors via their DirPort.

... and contain the following sections...

* document header
* list of :class:`stem.descriptor.networkstatus.DirectoryAuthority`
* list of :class:`stem.descriptor.router_status_entry.RouterStatusEntry`
* document footer

**For a great graphical overview see** `Jordan Wright's chart describing the
anatomy of the consensus
<https://jordan-wright.github.io/images/blog/how_tor_works/consensus.png>`_.

Of these, the router status entry section can be quite large (on the order of
hundreds of kilobytes). As such we provide a couple of methods for reading
network status documents through :func:`~stem.descriptor.__init__.parse_file`.
For more information see :func:`~stem.descriptor.__init__.DocumentHandler`...

::

  from stem.descriptor import parse_file, DocumentHandler

  with open('.tor/cached-consensus', 'rb') as consensus_file:
    # Processes the routers as we read them in. The routers refer to a document
    # with an unset 'routers' attribute.

    for router in parse_file(consensus_file, 'network-status-consensus-3 1.0', document_handler = DocumentHandler.ENTRIES):
      print router.nickname

**Module Overview:**

::

  NetworkStatusDocument - Network status document
    |- NetworkStatusDocumentV2 - Version 2 network status document
    |- NetworkStatusDocumentV3 - Version 3 network status document
    +- BridgeNetworkStatusDocument - Version 3 network status document for bridges

  KeyCertificate - Certificate used to authenticate an authority
  DocumentSignature - Signature of a document by a directory authority
  DetachedSignature - Stand alone signature used when making the consensus
  DirectoryAuthority - Directory authority as defined in a v3 network status document
    N)PGP_BLOCK_END
Descriptor
DigestHashDigestEncodingTypeAnnotationDocumentHandler_descriptor_content_descriptor_components_read_until_keywords_value_values_parse_simple_line_parse_if_present_parse_timestamp_line_parse_forty_character_hex_parse_protocol_line_parse_key_block_mappings_for_random_nickname_random_fingerprint_random_ipv4_address_random_date_random_crypto_blob)RouterStatusEntryV2RouterStatusEntryBridgeV2RouterStatusEntryV3RouterStatusEntryMicroV3)
)network-status-versionT)
dir-sourceTfingerprintT)contactTdir-signing-keyT)client-versionsF)server-versionsF)	publishedT)dir-optionsF)directory-signatureT))r   TTT)vote-statusTTT)consensus-methodsTFF)consensus-methodFTF)r&   TFT)valid-afterTTT)fresh-untilTTT)valid-untilTTT)voting-delayTTT)r$   TTF)r%   TTF)packageTTF)known-flagsTTT)flag-thresholdsTFF)shared-rand-participateTFF)shared-rand-commitTFF)shared-rand-previous-valueTTF)shared-rand-current-valueTTF)bandwidth-file-headersTFF)bandwidth-file-digestTFF)recommended-client-protocolsTTF)recommended-relay-protocolsTTF)required-client-protocolsTTF)required-relay-protocolsTTF)paramsTTF))directory-footerTTF)bandwidth-weightsFTF)r(   TTTr   rr>   r(   Zbwweightscale'  ZcbtdisabledZcbtnummodes   Zcbtrecentcount   Zcbtmaxtimeouts   Zcbtmincircsd   cbtquantileP   cbtclosequantile_   Zcbttestfreq<   cbtmintimeouti  cbtinitialtimeout`  Zcbtlearntimeout   Zcbtmaxopencircs
   ZUseOptimisticData   ZSupport022HiddenServicesZusecreatefastz%max-consensuses-age-to-cache-for-diffH            P    )!try-diff-for-consensus-newer-thanonion-key-rotation-daysonion-key-grace-period-dayshs_service_max_rdv_failurescirc_max_cell_queue_sizecircpad_max_circ_queued_cells"HiddenServiceEnableIntroDoSDefense)	)dir-key-certificate-versionT)dir-addressFr   )dir-identity-keyT)dir-key-publishedT)dir-key-expiresTr"   )dir-key-crosscertF)dir-key-certificationT))consensus-digestTF)r,   TF)r-   TF)r.   TF)additional-digestFT)additional-signatureFT)r(   FT)i   iZ
circwindow)rE   rV   ZCircuitPriorityHalflifeMsecZperconnbwrateZperconnbwburstZrefuseunknownexits)r   rP   )rP   rC   )rB   rV   )rB   rA   )rP   rA   )rO   c   ri   i  )rO   rM   )r      ZUseNTorHandshakeZFastFlagMinThreshold   ZNumDirectoryGuards)r   rO   ZNumEntryGuards)rP   rO   ZGuardLifetime)i ' i Sg	ZNumNTorsPerTAP)rP   i ZAllowNonearlyExtendZAuthDirNumSRVAgreements)r   i    rW   rX   )rP   Z   rY   rZ   r[   )rV   l    )r   rU   )r\   r]   c                   @      e Zd ZdZdS )PackageVersionz
  Latest recommended version of a package that's available.

  :var str name: name of the package
  :var str version: latest recommended version
  :var str url: package's url
  :var dict digests: mapping of digest types to their value
  N__name__
__module____qualname____doc__ rt   rt   ?/usr/lib/python3/dist-packages/stem/descriptor/networkstatus.pyrn         rn   )nameversionurldigestsc                   @   rm   )SharedRandomnessCommitmenta  
  Directory authority's commitment for generating the next shared random value.

  :var int version: shared randomness protocol version
  :var str algorithm: hash algorithm used to make the commitment
  :var str identity: authority's sha1 identity fingerprint
  :var str commit: base64 encoded commitment hash to the shared random value
  :var str reveal: base64 encoded commitment to the shared random value,
    **None** of not provided
  Nro   rt   rt   rt   ru   r{     rv   r{   )rx   	algorithmidentitycommitrevealc                   @   rm   )DocumentDigestz
  Digest of a consensus document.

  .. versionadded:: 1.8.0

  :var str flavor: consensus type this digest is for (for example, 'microdesc')
  :var str algorithm: hash algorithm used to make the digest
  :var str digest: digest value of the consensus
  Nro   rt   rt   rt   ru   r     rv   r   )flavorr|   digestFc              	   k   s   |du rt }|tkrtt}}n-|t kr|rtnt}n"|tkr&tt}}n|tkr8||  |fi |V  dS t	d| |t
jkrQ||  |fi |V  dS ttttf| }|rh|d drh|dd }|  }tttf| dd |  }	|  }
td||
 }|t
jkr|||fi |V  dS |t
jkrtjjj| |f|t||	|||fd	|}|D ]}|V  qdS t	d
| )a  
  Parses a network status and iterates over the RouterStatusEntry in it. The
  document that these instances reference have an empty 'routers' attribute to
  allow for limited memory usage.

  :param file document_file: file with network status document content
  :param class document_type: NetworkStatusDocument subclass
  :param bool validate: checks the validity of the document's contents if
    **True**, skips these checks otherwise
  :param bool is_microdescriptor: **True** if this is for a microdescriptor
    consensus, **False** otherwise
  :param stem.descriptor.__init__.DocumentHandler document_handler: method in
    which to parse :class:`~stem.descriptor.networkstatus.NetworkStatusDocument`
  :param dict kwargs: additional arguments for the descriptor constructor

  :returns: :class:`stem.descriptor.networkstatus.NetworkStatusDocument` object

  :raises:
    * **ValueError** if the document_version is unrecognized or the contents is
      malformed and validate is **True**
    * **IOError** if the file can't be read
  NzIDocument type %i isn't recognized (only able to parse v2, v3, and bridge)r   s   @typerP   T)skip    )entry_classentry_keywordZstart_positionZend_position
extra_argsz!Unrecognized document_handler: %s)NetworkStatusDocumentV3NetworkStatusDocumentV2r   r   r   BridgeNetworkStatusDocumentr   DetachedSignatureread
ValueErrorr   ZDOCUMENTr
   ROUTERS_STARTFOOTER_STARTV2_FOOTER_START
startswithtell	readlinesbytesjoinZBARE_DOCUMENTENTRIESstem
descriptorrouter_status_entry_parse_file)document_fileZdocument_typevalidateis_microdescriptorZdocument_handlerkwargsZrouter_typeheaderZrouters_startZrouters_endZfooterdocument_contentZdesc_iteratordescrt   rt   ru   r   (  sV   



r   c                 c   sV    	 t d| }tddd }|t || d7 }|r(tjjjtd||dV  ndS q)	a  
  Parses a file containing one or more authority key certificates.

  :param file certificate_file: file with key certificates
  :param bool validate: checks the validity of the certificate's contents if
    **True**, skips these checks otherwise

  :returns: iterator for :class:`stem.descriptor.networkstatus.KeyCertificate`
    instances in the file

  :raises:
    * **ValueError** if the key certificates are invalid and validate is **True**
    * **IOError** if the file can't be read
  Trd    rP   r   r   r   N)	r
   r   splitr   r   networkstatusKeyCertificater   r   )Zcertificate_filer   Zkeycert_contentZblock_end_prefixrt   rt   ru   _parse_file_key_certsw  s   
r   c                 c   s:    	 t d| dd}|rtjjjtd||dV  ndS q)a  
  Parses a file containing one or more detached signatures.

  :param file detached_signature_file: file with detached signatures
  :param bool validate: checks the validity of the detached signature's
    contents if **True**, skips these checks otherwise

  :returns: iterator for :class:`stem.descriptor.networkstatus.DetachedSignature`
    instances in the file

  :raises:
    * **ValueError** if the detached signatures are invalid and validate is **True**
    * **IOError** if the file can't be read
  Tre   )Zignore_firstr   r   N)r
   r   r   r   r   r   r   )Zdetached_signature_filer   Zdetached_sig_contentrt   rt   ru   _parse_file_detached_sigs  s   r   c                   @   s"   e Zd ZdZejejfddZdS )NetworkStatusDocumentz1
  Common parent for network status documents.
  c                 C   sT   | j dd}|tjkrtjt||S |tjkr$tjt	||S t
d| )a  
    Digest of this descriptor's content. These are referenced by...

      * **DetachedSignature**

        * Referer: :class:`~stem.descriptor.networkstatus.DetachedSignature` **consensus_digest** attribute
        * Format: **SHA1/HEX**

    .. versionadded:: 1.8.0

    :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
    z
directory-signature )endzMNetwork status document digests are only available in sha1 and sha256, not %s)_content_ranger   SHA1r   r   Z_encode_digesthashlibsha1ZSHA256Zsha256NotImplementedError)selfZ	hash_typeencodingcontentrt   rt   ru   r     s   

zNetworkStatusDocument.digestN)	rp   rq   rr   rs   r   r   r   ZHEXr   rt   rt   rt   ru   r     s    r   c                    s    fdd}|S )Nc                    sR   t |}| std|f t|  t| t|kr'td|f d S )Nz)Document has a non-numeric version: %s %sz<Expected a version %i document, but got version '%s' instead)r   isdigitr   setattrintr   entriesvalue	attributeexpected_versionkeywordrt   ru   _parse  s   
z#_parse_version_line.<locals>._parsert   )r   r   r   r   rt   r   ru   _parse_version_line  s   r   c                 C   s   t d|}| }t|dk rtd| |d std| tjj|d s0td|d  tjjj|d d	d
sCtd|d  |d | _	|d | _
|d dkrXd | _d S t|d | _d S )Nr   rB   z[The 'dir-source' line of a v2 network status document must have three values: dir-source %sr   2Authority's hostname can't be blank: dir-source %srP   2Authority's address isn't a valid IPv4 address: %srT   TZ
allow_zero"Authority's DirPort is invalid: %s0)r   r   lenr   r   util
connectionis_valid_ipv4_addressis_valid_porthostnameaddressr   dir_portr   r   r   Zdir_source_comprt   rt   ru   _parse_dir_source_line  s   


(r   c                 C   sV   g }t d|D ]}|d}t|dk rtd| |t|d d   q|| _d S )Nrf   r   rB   ziadditional-digest lines should be of the form 'additional-digest [flavor] [algname] [digest]' but was: %s)r   r   r   r   appendr   additional_digests)r   r   rz   valcomprt   rt   ru   _parse_additional_digests  s   

r   c                 C   s   g }|d D ]7\}}}| d}t|dk rtd| |r"|dkr(td| |t|d |d |d	 ||d
 dd q|| _d S )Nrg   r   rk   zadditional-signature lines should be of the form 'additional-signature [flavor] [algname] [identity] [signing_key_digest]' but was: %s	SIGNATUREzL'additional-signature' should be followed by a SIGNATURE block, but was a %srP   rT   rB   r   T)r   r   )r   r   r   r   DocumentSignatureadditional_signatures)r   r   
signaturesr   
block_typeblock_contentsr   rt   rt   ru   _parse_additional_signatures  s   
,
r   r   rx   r    r!   r#   signing_keyRSA PUBLIC KEYr$   client_versionsc                 C   
   |  dS N,r   vrt   rt   ru   <lambda>     
 r   )funcr%   server_versionsc                 C   r   r   r   r   rt   rt   ru   r     r   r&   r'   optionsc                 C   s   |   S Nr   r   rt   rt   ru   r         	signaturer   signing_authority)Zvalue_attributere   consensus_digestc                       s   e Zd 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g e
fg efdefg efdefdefdZeeeee	e
eeeed
Zeddd	Zd fd
d	Zdd Z  ZS )r   a  
  Version 2 network status document. These have been deprecated and are no
  longer generated by Tor.

  :var dict routers: fingerprints to :class:`~stem.descriptor.router_status_entry.RouterStatusEntryV2`
    contained in the document

  :var int version: **\*** document version

  :var str hostname: **\*** hostname of the authority
  :var str address: **\*** authority's IP address
  :var int dir_port: **\*** authority's DirPort
  :var str fingerprint: **\*** authority's fingerprint
  :var str contact: **\*** authority's contact information
  :var str signing_key: **\*** authority's public signing key

  :var list client_versions: list of recommended client tor version strings
  :var list server_versions: list of recommended server tor version strings
  :var datetime published: **\*** time when the document was published
  :var list options: **\*** list of things that this authority decides

  :var str signing_authority: **\*** name of the authority signing the document
  :var str signature: **\*** authority's signature for the document

  **\*** attribute is either required when we're parsed with validation or has
  a default value, others are left as **None** if undefined
  znetwork-status-2N)rx   r   r   r   r    r!   r   r   r   r&   r   r   r   )
r   r   r    r!   r#   r$   r%   r&   r'   r(   rt   Fc                 C   s^   |r	t d| j t||dddt t f fdt fddt fdtd	ffd
dtd ffS )NSigning of %s not implemented)r   2r   z%s %s 80r    )r!   zarma at mit dot edur&   r#   r   r(   Zmoria2r   )r   rp   r   r   r   r   r   clsattrexcludesignrt   rt   ru   r   X  s   
zNetworkStatusDocumentV2.contentc                    s   t t| j|| d t|}tdttt	f|}t
jjj||ttt	f| fd}tdd |D | _t|d |  |}|re| | | || d| jv rad|v rYd	|v sctd
t|  d S d S || _d S )NZ	lazy_loadr   r   r   Zsection_end_keywordsr   c                 s       | ]}|j |fV  qd S r   r    .0r   rt   rt   ru   	<genexpr>|      z3NetworkStatusDocumentV2.__init__.<locals>.<genexpr>   
ZVersionsr$   r%   zVersion 2 network status documents must have a 'client-versions' and 'server-versions' when 'Versions' is listed among its dir-options:
%s)superr   __init__ioBytesIOr   r   r
   r   r   r   r   r   r   r   dictroutersr	   r   _check_constraintsr   r   r   str_entries)r   raw_contentr   r   r   router_iterr   	__class__rt   ru   r   h  s(   
	

z NetworkStatusDocumentV2.__init__c                 C   s   dd t D }|D ]}||vrtd|t| f q	dd t D }|D ]}||v r@t|| dkr@td|t|| t| f q#dt| d krStd	t|  d S )
Nc                 S   s   g | ]\}}|r|qS rt   rt   )r   fieldis_mandatoryrt   rt   ru   
<listcomp>      z>NetworkStatusDocumentV2._check_constraints.<locals>.<listcomp>z6Network status document (v2) must have a '%s' line:
%sc                 S   s   g | ]\}}|qS rt   rt   )r   r
  _rt   rt   ru   r        rP   zINetwork status document (v2) can only have a single '%s' line, got %i:
%sr   r   z[Network status document (v2) are expected to start with a 'network-status-version' line:
%s)NETWORK_STATUS_V2_FIELDSr   r  r   listkeys)r   r   required_fieldsr   Zsingle_fieldsrt   rt   ru   r    s   z*NetworkStatusDocumentV2._check_constraintsNrt   FF)rp   rq   rr   rs   TYPE_ANNOTATION_NAME"_parse_network_status_version_liner   _parse_fingerprint_line_parse_contact_line_parse_dir_signing_key_line_parse_client_versions_line_parse_server_versions_line_parse_published_line_parse_dir_options_line_parse_directory_signature_line
ATTRIBUTESPARSER_FOR_LINEclassmethodr   r   r  __classcell__rt   rt   r  ru   r     s@    $r   c                 C   sx   t d|}d|v r|dd\}}n|d}}| s!td| t|| _|| _|dk| _| jdkr:td| j d S )	Nr   r   rP   nszLNetwork status document has a non-numeric version: network-status-version %sZ	microdescrB   zFExpected a version 3 network status document, got version '%s' instead)r   r   r   r   r   rx   version_flavorr   )r   r   r   rx   r   rt   rt   ru   )_parse_header_network_status_version_line  s   




r&  c                 C   sF   t d|}|dkrd\| _| _d S |dkrd\| _| _d S td| )Nr)   	consensus)TFvoteFTz`A network status document's vote-status line can only be 'consensus' or 'vote', got '%s' instead)r   is_consensusis_voter   r   rt   rt   ru   _parse_header_vote_status_line  s   
r,  c                 C   s`   | j r
| jr
dg| _td|g }}|dD ]}| s#td| |t| q|| _d S )NrP   r*   r   z\A network status document's consensus-methods must be a list of integer values, but was '%s')	_lazy_loadingr+  consensus_methodsr   r   r   r   r   r   )r   r   r   r.  entryrt   rt   ru   $_parse_header_consensus_methods_line  s   
r0  c                 C   s>   | j r	| jr	d| _td|}| std| t|| _d S )NrP   r+   zMA network status document's consensus-method must be an integer, but was '%s')r-  r*  consensus_methodr   r   r   r   r   rt   rt   ru   #_parse_header_consensus_method_line  s   
r2  c                 C   sd   t d|}|d}t|dkr,|d  r,|d  r,t|d | _t|d | _d S td| )Nr/   r   rT   r   rP   z^A network status document's 'voting-delay' line must be a pair of integer values, but was '%s')r   r   r   r   r   
vote_delay
dist_delayr   r   r   r   
value_comprt   rt   ru   _parse_header_voting_delay_line  s   

$r7  c                    s    fdd}|S )Nc              
      sh   t |g }}|dD ]}z|tj| W q ty+   td||f w t|  | d S )Nr   zWNetwork status document's '%s' line had '%s', which isn't a parsable tor version: %s %s)r   r   r   r   rx   Z_get_versionr   r   )r   r   r   r/  r   r   rt   ru   r     s   z$_parse_versions_line.<locals>._parsert   )r   r   r   rt   r8  ru   _parse_versions_line  s   r9  c              	   C   s   t d| i }}td|D ];\}}z*|dr+td|d d ddd ||< nd|v r6t|||< nt|||< W q tyJ   td| w || _d S )	Nr2   %z0.rh   . rP   zjNetwork status document's 'flag-thresholds' line is expected to have float values, got: flag-thresholds %s)	r   stripr   endswithfloatreplacer   r   flag_thresholds)r   r   r   Z
thresholdskeyr   rt   rt   ru   "_parse_header_flag_thresholds_line  s   
$
rC  c                 C   sJ   | j r| jr
ttni | _td|}|dkr#td|d| _|   d S d S )Nr=   r<  T)r-  _default_paramsr  DEFAULT_PARAMSr=   r   _parse_int_mappings_check_params_constraintsr   rt   rt   ru   _parse_header_parameters_line  s   
rH  c                 C   s   t d|}|rtd| d S )Nr>   ziA network status document's 'directory-footer' line shouldn't have any content, got 'directory-footer %s')r   r   r   rt   rt   ru   _parse_directory_footer_line$  s   
rI  c           	   
   C   s   g }|d D ]E\}}}| ddvrtd| |r|dkr$td| | ddkr6d}|dd\}}n	|dd	\}}}|t||||d
d q|| _d S )Nr(   r   )rP   rT   zAuthority signatures in a network status document are expected to be of the form 'directory-signature [METHOD] FINGERPRINT KEY_DIGEST', received: %sr   zK'directory-signature' should be followed by a SIGNATURE block, but was a %srP   r   rT   Tr   )countr   r   r   r   r   )	r   r   r   Z	sig_valuer   r   methodr    
key_digestrt   rt   ru   &_parse_footer_directory_signature_line-  s   
rM  c                 C   s   g }|d D ]B\}}}| dd}t|dk rtd| |d d \}}}i }	t|dkr>td|d D ]\}
}||	|
< q5|t||||	 q|| _d S )Nr0   r   rB   z<'package' must at least have a 'PackageName Version URL': %srk   )r   r   r   r   r   rn   packages)r   r   Zpackage_versionsr   r  r6  rw   rx   ry   rz   rB  r   rt   rt   ru   _parse_package_lineB  s   

rO  c              	   C   s   g }|d D ]B\}}}|  }t|dk rtd| |d d \}}}}	t|dkr/|d nd }
| s;td| |tt||||	|
 q|| _d S )Nr4   rk   zO'shared-rand-commit' must at least have a 'Version AlgName Identity Commit': %s   zBThe version on our 'shared-rand-commit' line wasn't an integer: %s)r   r   r   r   r   r{   r   shared_randomness_commitments)r   r   Zcommitmentsr   r  r6  rx   r|   r}   r~   r   rt   rt   ru   _parsed_shared_rand_commitW  s   
rR  c                 C   T   t d|}|d}t|dkr$|d  r$t|d | _|d | _d S td| )Nr5   r   rT   r   rP   zyA network status document's 'shared-rand-previous-value' line must be a pair of values, the first an integer but was '%s')r   r   r   r   r   'shared_randomness_previous_reveal_count shared_randomness_previous_valuer   r5  rt   rt   ru   !_parse_shared_rand_previous_valuem     

rV  c                 C   rS  )Nr6   r   rT   r   rP   zxA network status document's 'shared-rand-current-value' line must be a pair of values, the first an integer but was '%s')r   r   r   r   r   &shared_randomness_current_reveal_countshared_randomness_current_valuer   r5  rt   rt   ru    _parse_shared_rand_current_valuez  rW  rZ  c                 C   4   t d|}i }td|D ]\}}|||< q|| _d S )Nr7   )r   r   bandwidth_file_headersr   r   r   resultsrB  r   rt   rt   ru   _parse_bandwidth_file_headers  s
   


r_  c                 C   r[  )Nr8   )r   r   bandwidth_file_digestr]  rt   rt   ru   _parse_bandwidth_file_digest  s
   


ra  r,   valid_afterr-   fresh_untilr.   valid_untilr1   known_flagsc                 C   s   dd |  dD S )Nc                 S   s   g | ]}|r|qS rt   rt   )r   r/  rt   rt   ru   r    r  z<lambda>.<locals>.<listcomp>r   r   r   rt   rt   ru   r     r  r?   bandwidth_weightsc                 C   s   t d| dS )Nr?   T)rF  r   rt   rt   ru   r     s    r3    is_shared_randomness_participater9   recommended_client_protocolsr:   recommended_relay_protocolsr;   required_client_protocolsr<   required_relay_protocolsc                       s  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g 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g efdg efdg efdg efi efi efi efi efi efi efdefdefdefdefi efi efg efi 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d'eeeeeeed(Zeeed)Z e!dCd+d,Z"e!dDd-d.Z#dE fd/d0	Z$d1d2 Z%d3d4 Z&d5d6 Z'd7d8 Z( fd9d:Z)d;d< Z*d=d> Z+d?d@ Z,dAdB Z-  Z.S )Fr   a  
  Version 3 network status document. This could be either a vote or consensus.

  :var dict routers: fingerprint to :class:`~stem.descriptor.router_status_entry.RouterStatusEntryV3`
    mapping for relays contained in the document

  :var int version: **\*** document version
  :var str version_flavor: **\*** flavor associated with the document (such as 'ns' or 'microdesc')
  :var bool is_consensus: **\*** **True** if the document is a consensus
  :var bool is_vote: **\*** **True** if the document is a vote
  :var bool is_microdescriptor: **\*** **True** if this is a microdescriptor
    flavored document, **False** otherwise
  :var datetime valid_after: **\*** time when the consensus became valid
  :var datetime fresh_until: **\*** time when the next consensus should be produced
  :var datetime valid_until: **\*** time when this consensus becomes obsolete
  :var int vote_delay: **\*** number of seconds allowed for collecting votes
    from all authorities
  :var int dist_delay: **\*** number of seconds allowed for collecting
    signatures from all authorities
  :var list client_versions: list of recommended client tor versions
  :var list server_versions: list of recommended server tor versions
  :var list packages: **\*** list of :data:`~stem.descriptor.networkstatus.PackageVersion` entries
  :var list known_flags: **\*** list of :data:`~stem.Flag` for the router's flags
  :var dict params: **\*** dict of parameter(**str**) => value(**int**) mappings
  :var list directory_authorities: **\*** list of :class:`~stem.descriptor.networkstatus.DirectoryAuthority`
    objects that have generated this document
  :var list signatures: **\*** :class:`~stem.descriptor.networkstatus.DocumentSignature`
    of the authorities that have signed the document

  **Consensus Attributes:**

  :var int consensus_method: method version used to generate this consensus
  :var dict bandwidth_weights: dict of weight(str) => value(int) mappings

  :var int shared_randomness_current_reveal_count: number of commitments
    used to generate the current shared random value
  :var str shared_randomness_current_value: base64 encoded current shared
    random value

  :var int shared_randomness_previous_reveal_count: number of commitments
    used to generate the last shared random value
  :var str shared_randomness_previous_value: base64 encoded last shared random
    value

  **Vote Attributes:**

  :var list consensus_methods: list of ints for the supported method versions
  :var datetime published: time when the document was published
  :var dict flag_thresholds: **\*** mapping of internal performance thresholds used while making the vote, values are **ints** or **floats**

  :var dict recommended_client_protocols: recommended protocols for clients
  :var dict recommended_relay_protocols: recommended protocols for relays
  :var dict required_client_protocols: required protocols for clients
  :var dict required_relay_protocols: required protocols for relays
  :var dict bandwidth_file_headers: headers from the bandwidth authority that
    generated this vote
  :var dict bandwidth_file_digest: hashes of the bandwidth authority file used
    to generate this vote, this is a mapping of hash functions to their resulting
    digest value

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

  .. versionchanged:: 1.4.0
     Added the packages attribute.

  .. versionchanged:: 1.5.0
     Added the is_shared_randomness_participate, shared_randomness_commitments,
     shared_randomness_previous_reveal_count,
     shared_randomness_previous_value,
     shared_randomness_current_reveal_count, and
     shared_randomness_current_value attributes.

  .. versionchanged:: 1.6.0
     Added the recommended_client_protocols, recommended_relay_protocols,
     required_client_protocols, and required_relay_protocols attributes.

  .. versionchanged:: 1.6.0
     The is_shared_randomness_participate and shared_randomness_commitments
     were misdocumented in the tor spec and as such never set. They're now an
     attribute of votes in the **directory_authorities**.

  .. versionchanged:: 1.7.0
     The shared_randomness_current_reveal_count and
     shared_randomness_previous_reveal_count attributes were undocumented and
     not provided properly if retrieved before their shred_randomness_*_value
     counterpart.

  .. versionchanged:: 1.7.0
     Added the bandwidth_file_headers attributbute.

  .. versionchanged:: 1.8.0
     Added the bandwidth_file_digest attributbute.
  rx   Nr%  r$  r*  Tr+  Fr   r.  r&   r1  rb  rc  rd  r3  r4  r   r   rN  re  )rA  rh  ri  rj  rk  r=   rT  rU  rX  rY  r\  r`  r   rf  r   r)   r*   r+   r,   r-   r.   r/   r$   r%   r0   r1   r2   r9   r:   r;   )r<   r=   r5   r6   r7   r8   )r>   r?   r(   rt   c                 C   s
  |r	t d| j |d u ri nt|}|ddk}|r#dt d}nddi}|r4|d u r4tj|dg}| D ]\}}	|rC||v rCq8||vrK|	||< q8t||d	d
ddddt fdt fdt fddddddfddddt	 t	 t
df ff}
|rd|
v r|
dd }nd|
v r|
d d }n|r|
d!7 }
t|
d }tjjd"d#d$ |D d" }|
d | | |
|d   }
|rd|
v r|
dd }nd|
v r|
d d }n|r|
d!7 }
t|
d }tjjd"d%d$ |D d" }|
d | | |
|d   }
|
S )&Nr   r)   r(  z1 9)r*   r&   r+   9)r+  )r   3)r)   r'  )r*   N)r+   N)r&   Nr,   r-   r.   )r/   z300 300)r$   N)r%   N)r0   N)r1   zPAuthority BadExit Exit Fast Guard HSDir Named Running Stable Unnamed V2Dir Valid)r=   N)r>   r<  )r?   Nr(   z%s %s%sr   s   directory-footers   
directory-footerrP   s   directory-signatures   
directory-signaturer   
c                 S      g | ]}t |qS rt   r  )r   art   rt   ru   r    r  z3NetworkStatusDocumentV3.content.<locals>.<listcomp>c                 S   ro  rt   rp  )r   r@   rt   rt   ru   r    r  )r   rp   r  getr   DirectoryAuthoritycreateitemsr   r   r   findr   r   r   	str_toolsZ	_to_bytesr   )r   r   r   r   authoritiesr  r+  Zextra_defaultskr   Zdesc_contentZ
footer_divZauthority_contentZrouter_contentrt   rt   ru   r   S  sr   ""zNetworkStatusDocumentV3.contentc                 C   s   | |  ||||||dS )Nr   r   )r   r   r   r   r   rx  r  rt   rt   ru   rt       zNetworkStatusDocumentV3.createc              	      s   t t| j|| d t|}d| _g | _|| _| || t	t
jjj||tttttf| jfd| _|rL| jrLt| jdkrLtdt| j| jf t
jjj||| jrWtnttttf| fd}tdd |D | _| || dS )	a~  
    Parse a v3 network status document.

    :param str raw_content: raw network status document data
    :param bool validate: **True** if the document is to be validated, **False** otherwise
    :param bool default_params: includes defaults in our params dict, otherwise
      it just contains values from the document

    :raises: **ValueError** if the document is invalid
    r   Fr   rP   zPVotes should only have an authority entry for the one that issued it, got %i: %sc                 s   r   r   r   r   rt   rt   ru   r     r   z3NetworkStatusDocumentV3.__init__.<locals>.<genexpr>N)r   r   r   r   r   rg  rQ  rD  _headertupler   r   r   r   rs  
AUTH_STARTr   r   r   r+  Zdirectory_authoritiesr   r   r   r   r   r  r  _footer)r   r  r   Zdefault_paramsr   r  r  rt   ru   r     s4   


		z NetworkStatusDocumentV3.__init__c                 C   sD   t | trtdddS | jst| jsdddS dddS tdddS )Nbridge-network-statusrP   r   znetwork-status-consensus-3znetwork-status-vote-3z$network-status-microdesc-consensus-3)
isinstancer   r   r   r+  r   rt   rt   ru   type_annotation  s
   
z'NetworkStatusDocumentV3.type_annotationc                 C   "   | j tj   k o| jk S   S )aJ  
    Checks if the current time is between this document's **valid_after** and
    **valid_until** timestamps. To be valid means the information within this
    document reflects the current network state.

    .. versionadded:: 1.8.0

    :returns: **True** if this consensus is presently valid and **False**
      otherwise
    )rb  datetimeutcnowrd  r  rt   rt   ru   is_valid     "z NetworkStatusDocumentV3.is_validc                 C   r  )a&  
    Checks if the current time is between this document's **valid_after** and
    **fresh_until** timestamps. To be fresh means this should be the latest
    consensus.

    .. versionadded:: 1.8.0

    :returns: **True** if this consensus is presently fresh and **False**
      otherwise
    )rb  r  r  rc  r  rt   rt   ru   is_fresh  r  z NetworkStatusDocumentV3.is_freshc           
      C   s   |  dd}t|  }d\}}t| jd }tdd |D }| jD ]}|j|vr.q&| 	||j |j
}	|d7 }|	|krD|d7 }q&||k rRtd|||f d	S )
a7  
    Validates we're properly signed by the signing certificates.

    .. versionadded:: 1.6.0

    :param list key_certs: :class:`~stem.descriptor.networkstatus.KeyCertificates`
      to validate the consensus against

    :raises: **ValueError** if an insufficient number of valid signatures are present.
    r   zdirectory-signature )r   r   g       @c                 S   s   g | ]}|j |jfqS rt   )r    r   )r   Zcertrt   rt   ru   r    r  z?NetworkStatusDocumentV3.validate_signatures.<locals>.<listcomp>rP   zJNetwork Status Document has %i valid signatures out of %i total, needed %iN)r   r   r   Z	hexdigestupperr   r   r  r}   Z_digest_for_signaturer   r   )
r   Z	key_certsZdigest_contentZlocal_digestZvalid_digestsZtotal_digestsZrequired_digestsZsigning_keysZsigZsigned_digestrt   rt   ru   validate_signatures  s    

z+NetworkStatusDocumentV3.validate_signaturesc                    sB   | j r| j| jd| jd | j| jd| jd d| _ tt|  S )NFZparser_for_line)	r-  r   _header_entries_HEADER_PARSER_FOR_LINE_footer_entries_FOOTER_PARSER_FOR_LINEr   r   get_unrecognized_linesr  r  rt   ru   r    s
   z.NetworkStatusDocumentV3.get_unrecognized_linesc                    s:   | j dur
| j  kS | jdurt fdd| jD S dS )a8  
    Checks if we meet the given consensus-method. This works for both votes and
    consensuses, checking our 'consensus-method' and 'consensus-methods'
    entries.

    :param int method: consensus-method to check for

    :returns: **True** if we meet the given consensus-method, and **False** otherwise
    Nc                    s   g | ]}| kr|qS rt   rt   )r   xrK  rt   ru   r  3  r  zBNetworkStatusDocumentV3.meets_consensus_method.<locals>.<listcomp>F)r1  r.  bool)r   rK  rt   r  ru   meets_consensus_method%  s
   


z.NetworkStatusDocumentV3.meets_consensus_methodc                 C   s  t dttttf|}t||}dd tD }|rt|	 D ] \}}t
|dkr@||v r@|dkr@|dkr@td|t
|f q | jrItt| _| j||| jd | d	scd
t| v rctdt| |t | jrt| jstd| _d S | jr| jsdg| _d S d S d S || _| j| d S )Nr   c                 S      g | ]}|d  qS r   rt   r   r   rt   rt   ru   r  :  r  z3NetworkStatusDocumentV3._header.<locals>.<listcomp>rP   r0   r4   ANetwork status documents can only have a single '%s' line, got %ir  rS   r=   z[A network status document's 'params' line should only appear in consensus-method 7 or later)r   r   r
   r~  r   r   r	   HEADER_STATUS_DOCUMENT_FIELDSr  ru  r   r   rD  r  rE  r=   r   r  r  r  (_check_for_missing_and_disallowed_fieldsr*  r1  r+  r.  r  r  update)r   r   r   r   r   Zheader_fieldsr   valuesrt   rt   ru   r|  7  s*   
$

zNetworkStatusDocumentV3._headerc                 C   s   t | |}dd tD }|rmt| D ]\}}t|dkr5||v r5|dkr+| js5td|t|f q| j||| j	d |rk| 
drUt| d d	krTtd
nt| d dkrctdt| |t d S d S || _| j| d S )Nc                 S   r  r  rt   r  rt   rt   ru   r  [  r  z3NetworkStatusDocumentV3._footer.<locals>.<listcomp>rP   r(   r  r  	   r   r>   zkNetwork status document's footer should start with a 'directory-footer' line in consensus-method 9 or laterzkNetwork status document's footer should start with a 'directory-signature' line prior to consensus-method 9)r	   r   FOOTER_STATUS_DOCUMENT_FIELDSr  ru  r   r*  r   r   r  r  r  r  r  r  r  )r   r   r   r   Zfooter_fieldsr   r  rt   rt   ru   r  Y  s(   

zNetworkStatusDocumentV3._footerc                 C   s~   | j  D ]7\}}t|ttf\}}|dkr| j d|}n|dkr*| j d|}||k s2||kr<td||||f qdS )zR
    Checks that the params we know about are within their documented ranges.
    rH   rF   rL   rK   zE'%s' value on the params line must be in the range of %i - %i, was %iN)r=   ru  PARAM_RANGErr  	MIN_PARAM	MAX_PARAMr   )r   rB  r   ZminimumZmaximumrt   rt   ru   rG  y  s   z1NetworkStatusDocumentV3._check_params_constraints)Nrt   FNN)Nrt   TFNNr)  )/rp   rq   rr   rs   r&  r,  r0  r  r2  _parse_header_valid_after_line_parse_header_fresh_until_line_parse_header_valid_until_liner7  "_parse_header_client_versions_line"_parse_header_server_versions_linerO  _parse_header_known_flags_linerC  (_parse_recommended_client_protocols_line'_parse_recommended_relay_protocols_line%_parse_required_client_protocols_line$_parse_required_relay_protocols_linerH  rV  rZ  r_  ra  rM  $_parse_footer_bandwidth_weights_liner   r  rI  r  r"  r   rt  r   r  r  r  r  r  r  r|  r  rG  r#  rt   rt   r  ru   r     s    _	
#	
J1"" r   c           	      C   s   g g }}|D ]3\}}}}|r%| j r|s| jr%|r%|| vr$|| q| j r*|r/| jr:|s:|| v r:|| q|rFtdd| |rQtdd| dS )a#  
  Checks that we have mandatory fields for our type, and that we don't have
  any fields exclusive to the other (ie, no vote-only fields appear in a
  consensus or vice versa).

  :param NetworkStatusDocumentV3 document: network status document
  :param dict entries: ordered keyword/value mappings of the header or footer
  :param list fields: expected field attributes (either
    **HEADER_STATUS_DOCUMENT_FIELDS** or **FOOTER_STATUS_DOCUMENT_FIELDS**)

  :raises: **ValueError** if we're missing mandatory fields or have fields we shouldn't
  z6Network status document is missing mandatory field: %sz, z]Network status document has fields that shouldn't appear in this document type or version: %sN)r*  r+  r  r   r   r   )	Zdocumentr   ZfieldsZmissing_fieldsZdisallowed_fieldsr
  Zin_votesZin_consensusZ	mandatoryrt   rt   ru   r    s   


r  c           	   	   C   s   i g }}d| |f }t | |D ]:\}}|r%|D ]}||kr$t|d qz|dr.t t|||< W n tyD   t|d|  w || q|S )Nz>Unable to parse network status document's '%s' line (%%s): %s'z&parameters must be sorted by their key+z'%s' is a non-numeric value)r   r   r   r   r   )	r   r   r   r^  Z	seen_keysZerror_templaterB  r   Z	prior_keyrt   rt   ru   rF    s"   

rF  c                 C   sZ  t d|}|d}t|dk rtd| tjj|d ds*td|d  tjj	|d s;td	|d  |d
 sEtd| tjj
|d sVtd|d  tjj
j|d ddsitd|d  tjj
|d sztd|d  |d | _|d | _|d
 | _|d | _|d dkrd nt|d | _t|d | _| jd| _d S )Nr   r      zGAuthority entry's 'dir-source' line must have six values: dir-source %sr   -legacyz#Authority's nickname is invalid: %srP   z"Authority's v3ident is invalid: %srT   r   rB   r   rk   Tr   r   rP  z!Authority's ORPort is invalid: %sr   )r   r   r   r   r   r   	tor_toolsZis_valid_nicknamerstripis_valid_fingerprintr   r   r   nicknamev3identr   r   r   r   or_portr>  	is_legacyr   rt   rt   ru   _parse_dirauth_source_line  s.   





r  legacy-dir-keylegacy_dir_keyvote-digestvote_digestc                	       s   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g e	fdde
fdde
fddefddefZeeeeee	e
edZedddZedddZd fdd	Z  ZS ) rs  aC	  
  Directory authority information obtained from a v3 network status document.

  Authorities can optionally use a legacy format. These are no longer found in
  practice, but have the following differences...

  * The authority's nickname ends with '-legacy'.
  * There's no **contact** or **vote_digest** attribute.

  :var str nickname: **\*** authority's nickname
  :var str v3ident: **\*** identity key fingerprint used to sign votes and consensus
  :var str hostname: **\*** hostname of the authority
  :var str address: **\*** authority's IP address
  :var int dir_port: **\*** authority's DirPort
  :var int or_port: **\*** authority's ORPort
  :var bool is_legacy: **\*** if the authority's using the legacy format
  :var str contact: contact information, this is included if is_legacy is **False**

  **Consensus Attributes:**

  :var str vote_digest: digest of the authority that contributed to the consensus, this is included if is_legacy is **False**

  **Vote Attributes:**

  :var str legacy_dir_key: fingerprint of and obsolete identity key
  :var stem.descriptor.networkstatus.KeyCertificate key_certificate: **\***
    authority's key certificate

  :var bool is_shared_randomness_participate: **\*** **True** if this authority
    participates in establishing a shared random value, **False** otherwise
  :var list shared_randomness_commitments: **\*** list of
    :data:`~stem.descriptor.networkstatus.SharedRandomnessCommitment` entries
  :var int shared_randomness_previous_reveal_count: number of commitments
    used to generate the last shared random value
  :var str shared_randomness_previous_value: base64 encoded last shared random
    value
  :var int shared_randomness_current_reveal_count: number of commitments
    used to generate the current shared random value
  :var str shared_randomness_current_value: base64 encoded current shared
    random value

  **\*** mandatory attribute

  .. versionchanged:: 1.4.0
     Renamed our 'fingerprint' attribute to 'v3ident' (prior attribute exists
     for backward compatability, but is deprecated).

  .. versionchanged:: 1.6.0
     Added the is_shared_randomness_participate, shared_randomness_commitments,
     shared_randomness_previous_reveal_count,
     shared_randomness_previous_value,
     shared_randomness_current_reveal_count, and
     shared_randomness_current_value attributes.
  r  Nr  r   r   r   r  r  Fr!   r  r  rg  rQ  rT  rU  rX  rY  )r   r!   r  r  r3   r4   r5   r6   rt   c                 C   s   |r	t d| j |d u ri nt|}|s$d|v s$|rd|v s$t |d< t||ddt t t f fdf}|r@|dt  7 }|S )Nr   r  r   z%s %s no.place.com %s 9030 9090)r!   zMike Perry <email>r   )	r   rp   r  r   r   r   r   r   r   )r   r   r   r   r+  r   rt   rt   ru   r   E  s   
zDirectoryAuthority.contentTc                 C   s   | |  ||||||dS )N)r   r+  rz  )r   r   r   r   r   r+  rt   rt   ru   rt  [  r{  zDirectoryAuthority.createc                    s  t t| j|| d tjj|}|d}|dkr0t||d d || _	|d|d  }nd| _	t
||}|rJdt| d krJtd| |rd	|d}}|rc|d d  d d
}dgg }	}
|sp|	dg7 }	|r| j	s{td| |
dg7 }
n|s| j	rtd| |s|	dg7 }	|
dg7 }
|	D ]}||vrtd||f q|D ]}||
v r|rdnd}td|||f qt| D ]\}}t|dkr|dv rtd|t||f q| || n|| _| j| _dS )a  
    Parse a directory authority entry in a v3 network status document.

    :param str raw_content: raw directory authority entry information
    :param bool validate: checks the validity of the content if True, skips
      these checks otherwise
    :param bool is_vote: True if this is for a vote, False if it's for a consensus

    :raises: ValueError if the descriptor data is invalid
    r   z
dir-key-certificate-versionrh   rP   Nr   r   zDAuthority entries are expected to start with a 'dir-source' line:
%sFr  r!   z/Authority votes must have a key certificate:
%sr  z@Authority consensus entries shouldn't have a key certificate:
%sr  z+Authority entries must have a '%s' line:
%sZvoteszconsensus entriesz+Authority %s shouldn't have a '%s' line:
%s)r   r!   r  r  z>Authority entries can only have a single '%s' line, got %i:
%s)r   rs  r   r   r   rw  _to_unicoderv  r   Zkey_certificater	   r  r  r   rr  r   r>  ru  r   r   r  r  r    )r   r  r   r+  r   Zkey_divr   r  Zdir_source_entryr  Zexcluded_fieldsr   Z
type_labelr  r  rt   ru   r   _  sV   




zDirectoryAuthority.__init__)Nrt   FF)Nrt   TFF)FF)rp   rq   rr   rs   r  r  _parse_vote_digest_line_parse_legacy_dir_key_line#_parse_shared_rand_participate_linerR  rV  rZ  r   r!  r"  r   rt  r   r#  rt   rt   r  ru   rs    sb    7	
rs  c                 C   sv   t d|}d|vrtd| |dd\}}tjj|s$td| tjj|s1td| || _t	|| _
d S )Nr_   :zZKey certificate's 'dir-address' is expected to be of the form ADDRESS:PORT: dir-address %srP   zDKey certificate's address isn't a valid IPv4 address: dir-address %sz4Key certificate's dirport is invalid: dir-address %s)r   r   rsplitr   r   r   r   r   r   r   r   )r   r   r   r   Zdirportrt   rt   ru   _parse_dir_address_line  s   
r  r^   ra   rb   expiresr`   identity_keyrc   	crosscertzID SIGNATURErd   certificationc                       s   e Zd 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
Zeeee	e
eeeed	Zeddd	Zd fd
d	Z  ZS )r   a  
  Directory key certificate for a v3 network status document.

  :var int version: **\*** version of the key certificate
  :var str address: authority's IP address
  :var int dir_port: authority's DirPort
  :var str fingerprint: **\*** authority's fingerprint
  :var str identity_key: **\*** long term authority identity key
  :var datetime published: **\*** time when this key was generated
  :var datetime expires: **\*** time after which this key becomes invalid
  :var str signing_key: **\*** directory server's public signing key
  :var str crosscert: signature made using certificate's signing key
  :var str certification: **\*** signature of this key certificate signed with
    the identity key

  **\*** mandatory attribute
  zdir-key-certificate-3N)
rx   r   r   r    r  r&   r  r   r  r  )	r^   r_   r    ra   rb   r`   r#   rc   rd   rt   Fc                 C   sX   |r	t d| j t||ddt fdt fdt fdtdfdtdffd	td
ffS )Nr   )r^   rm  r    ra   rb   r`   r   r#   rd   r   )r   rp   r   r   r   r   r   rt   rt   ru   r     s   


zKeyCertificate.contentc                    s   t t| j|| d t||}|rddt| d kr"td| dt| d kr2td| tD ]'\}}|rF||vrFtd||f t|	|g }|d	kr[td
|||f q4| 
|| d S || _d S )Nr   r^   r   zIKey certificates must start with a 'dir-key-certificate-version' line:
%srd   rh   zAKey certificates must end with a 'dir-key-certification' line:
%sz*Key certificates must have a '%s' line:
%srP   z=Key certificates can only have a single '%s' line, got %i:
%s)r   r   r   r	   r  r  r   KEY_CERTIFICATE_PARAMSr   rr  r   r  )r   r  r   r   r   r  entry_countr  rt   ru   r   
  s    

zKeyCertificate.__init__r  r  )rp   rq   rr   rs   r  '_parse_dir_key_certificate_version_liner  r  _parse_identity_key_line_parse_dir_key_published_line_parse_dir_key_expires_line_parse_signing_key_line_parse_dir_key_crosscert_line!_parse_dir_key_certification_liner   r!  r"  r   r   r#  rt   rt   r  ru   r     s6    r   c                   @   sJ   e Zd ZdZdddZdd Zdd	 Zd
d Zdd Zdd Z	dd Z
dS )r   a  
  Directory signature of a v3 network status document.

  :var str method: algorithm used to make the signature
  :var str identity: fingerprint of the authority that made the signature
  :var str key_digest: digest of the signing key
  :var str signature: document signature
  :var str flavor: consensus type this signature is for (such as 'microdesc'),
    **None** if for the standard consensus
  :param bool validate: checks validity if **True**

  :raises: **ValueError** if a validity check fails
  NFc                 C   sZ   |rt jj|std| t jj|std| || _|| _|| _|| _|| _	d S )Nz4Malformed fingerprint (%s) in the document signaturez3Malformed key digest (%s) in the document signature)
r   r   r  r  r   rK  r}   rL  r   r   )r   rK  r}   rL  r   r   r   rt   rt   ru   r   3  s   
zDocumentSignature.__init__c                 C   sP   t |tsdS dD ]}t| |t||kr"|t| |t||  S q	|ddS )NF)rK  r}   rL  r   r   T)r  r   getattr)r   otherrK  r   rt   rt   ru   _compareD  s   

zDocumentSignature._comparec                 C   s   t t|  S r   )hashr  r=  r  rt   rt   ru   __hash__N     zDocumentSignature.__hash__c                 C      |  |dd S )Nc                 S   s   | |kS r   rt   sort   rt   ru   r   R  r   z*DocumentSignature.__eq__.<locals>.<lambda>r  r   r  rt   rt   ru   __eq__Q  r  zDocumentSignature.__eq__c                 C   s
   | |k S r   rt   r  rt   rt   ru   __ne__T  s   
zDocumentSignature.__ne__c                 C   r  )Nc                 S   s   | |k S r   rt   r  rt   rt   ru   r   X  r   z*DocumentSignature.__lt__.<locals>.<lambda>r  r  rt   rt   ru   __lt__W  r  zDocumentSignature.__lt__c                 C   r  )Nc                 S   s   | |kS r   rt   r  rt   rt   ru   r   [  r   z*DocumentSignature.__le__.<locals>.<lambda>r  r  rt   rt   ru   __le__Z  r  zDocumentSignature.__le__)NF)rp   rq   rr   rs   r   r  r  r  r  r  r  rt   rt   rt   ru   r   $  s    

r   c                       sx   e Zd ZdZdZdefdefdefdefg e	fg e
fg efdZeeeee	e
edZeddd	Zd fd
d	Z  ZS )r   aG  
  Stand alone signature of the consensus. These are exchanged between directory
  authorities when determining the next hour's consensus.

  Detached signatures are defined in section 3.10 of the dir-spec, and only
  available to be downloaded for five minutes between minute 55 until the end
  of the hour.

  .. versionadded:: 1.8.0

  :var str consensus_digest: **\*** digest of the consensus being signed
  :var datetime valid_after: **\*** time when the consensus became valid
  :var datetime fresh_until: **\*** time when the next consensus should be produced
  :var datetime valid_until: **\*** time when this consensus becomes obsolete
  :var list additional_digests: **\***
    :class:`~stem.descriptor.networkstatus.DocumentDigest` for additional
    consensus flavors
  :var list additional_signatures: **\***
    :class:`~stem.descriptor.networkstatus.DocumentSignature` for additional
    consensus flavors
  :var list signatures: **\*** :class:`~stem.descriptor.networkstatus.DocumentSignature`
    of the authorities that have signed the document

  **\*** mandatory attribute
  zdetached-signature-3N)r   rb  rc  rd  r   r   r   )re   r,   r-   r.   rf   rg   r(   rt   Fc                 C   s8   |r	t d| j t||ddt fdt fdt ffS )Nr   )re   Z(6D3CC0EFA408F228410A4A8145E1B0BB0670E442r,   r-   r.   )r   rp   r   r   r   rt   rt   ru   r     s   zDetachedSignature.contentc                    s   t t| j|| d t||}|rWdt| d kr"td| tD ]*\}}}|r7||vr7td||f t|	|g }|sN|dkrNtd|||f q$| 
|| d S || _d S )Nr   re   r   zADetached signatures must start with a 'consensus-digest' line:
%sz-Detached signatures must have a '%s' line:
%srP   z@Detached signatures can only have a single '%s' line, got %i:
%s)r   r   r   r	   r  r  r   DETACHED_SIGNATURE_PARAMSr   rr  r   r  )r   r  r   r   r   r  Zis_multipler  r  rt   ru   r     s   

zDetachedSignature.__init__r  r  )rp   rq   rr   rs   r  _parse_consensus_digest_liner  r  r  r   r   rM  r   r!  r"  r   r   r#  rt   rt   r  ru   r   ^  s,    
r   c                       s&   e Zd ZdZdZd fdd	Z  ZS )r   a<  
  Network status document containing bridges. This is only available through
  the metrics site.

  :var dict routers: fingerprint to :class:`~stem.descriptor.router_status_entry.RouterStatusEntryV3`
    mapping for relays contained in the document
  :var datetime published: time when the document was published
  r  Fc                    s   t t| | d | _t|}tjj	|
 }|drD|ddd  }z
tjj|| _W n tyC   |rAtd| Y nw |rQtdtjj	| tjjj||t| fd}tdd |D | _d S )	Nz
published r   rP   zEBridge network status document's 'published' time wasn't parsable: %szFBridge network status documents must start with a 'published' line:
%s)r   r   c                 s   r   r   r   r   rt   rt   ru   r     r   z7BridgeNetworkStatusDocument.__init__.<locals>.<genexpr>)r   r   r   r&   r   r   r   r   rw  r  readliner   r   r=  Z_parse_timestampr   r   r   r   r   r  r  )r   r  r   r   Zpublished_liner  r  rt   ru   r     s,   

z$BridgeNetworkStatusDocument.__init__r  )rp   rq   rr   rs   r  r   r#  rt   rt   r  ru   r     s    	r   r  ){rs   collectionsr  r   r   Z#stem.descriptor.router_status_entryr   Zstem.util.str_toolsZstem.util.tor_toolsZstem.versionZ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   r   r   r  r  r  r~  r   r   r   rE  r  r  r  r  r  
namedtuplern   r{   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r  r  r  r  r  r   r&  r,  r0  r2  r7  r9  rC  rH  rI  rM  rO  rR  rV  rZ  r_  ra  r  r  r  r  r  r  r  r  r  r  r  r  r   r  rF  r  r  r  rs  r  r  r  r  r  r  r  r  r   objectr   r   r   rt   rt   rt   ru   <module>   s  6d
	
	
 !"#(
O
 



 	









   _!
 
 B

X:T