U
    c_                     @   sx  d dl Z d dlZd dlmZ d dlmZ d dlmZ d dlm	Z
 d dlmZ d dlmZ d dlmZmZ d d	lmZ d d
lmZmZ d dlmZ d dlmZ d dlmZ d dlmZ d dlm Z  ddl!m"Z" ddl#m$Z$m%Z% ddl&m'Z' ddddgZ(ddl)m*Z* ddl+m,Z, e-e.Z/eG dd dZ0dd Z1G dd dZ2d&e'ed!d"dZ3d d#d dd ej4d#fe'ee5d$d%dZ6dS )'    N)	dataclass)field)Optional)crl)ocsp)Certificate)CertificateValidatorValidationContext)ValidationPath)genericmisc)pdf_name)IncrementalPdfFileWriter)get_and_apply)
PdfHandler)BasePdfFileWriter   )extract_certificate_info   )NoDSSFoundErrorValidationInfoReadingError)EmbeddedPdfSignatureVRIDocumentSecurityStoreasync_add_validation_infocollect_validation_info   )SerialisedCredential)PdfFileReaderc                   @   sX   e Zd ZU dZeedZeed< eedZeed< eedZ	eed< e
jdddZd	S )
r   aA  
    VRI dictionary as defined in PAdES / ISO 32000-2.
    These dictionaries collect data that may be relevant for the validation of
    a specific signature.

    .. note::
        The data are stored as PDF indirect objects, not asn1crypto values.
        In particular, values are tied to a specific PDF handler.
    )default_factorycertsocspscrlsreturnc                 C   sb   t tdtdi}| jr0t | j|td< | jrJt | j|td< t | j|td< |S )zT
        :return:
            A PDF dictionary representing this VRI entry.
        z/Type/VRIz/OCSPz/CRLz/Cert)r   DictionaryObjectr   r!   ArrayObjectr"   r    )selfvri r*   ?/tmp/pip-unpacked-wheel-0kb_yl26/pyhanko/sign/validation/dss.pyas_pdf_object>   s    zVRI.as_pdf_objectN)__name__
__module____qualname____doc__
data_fieldsetr    __annotations__r!   r"   r   r&   r,   r*   r*   r*   r+   r   #   s
   

c                 c   sD   | d j }|dkr@| d }|d j dkr@|d j}|d E dH  dS )	zJ
    Essentially nabbed from _extract_ocsp_certs in ValidationContext
    Zresponse_statusZ
successfulresponse_bytesZresponse_typeZbasic_ocsp_responseresponser    N)Znativeparsed)Zocsp_responsestatusr4   r5   r*   r*   r+   enumerate_ocsp_certsL   s    

r8   c                
   @   s   e Zd ZdZd*edddZedd Zdd	 Zd
d Z	dd Z
dd ZeejdddZddddddZdd Zdd Zd+edddZeed ddd Zeddddddd!eed d"d#d$Zedddddd%ddd&eeee d'd(d)ZdS ),r   z,
    Representation of a DSS in Python.
    Nwriterc                 C   s   |d k	r|ni | _ |d k	r|ni | _|d k	r0|ng | _|d k	rB|ng | _|| _|d k	rZ|nt | _i }| jD ]}| j	}	|||	< qn|| _
i }
| jD ]}| j	}||
|< q|
| _d| _d S )NF)vri_entriesr    r!   r"   r:   r   r&   backing_pdf_object
get_objectdata_ocsps_seen
_crls_seen	_modified)r(   r:   r    r!   r"   r;   r<   Z
ocsps_seenocsp_refZ
ocsp_bytesZ	crls_seencrl_refZ	crl_bytesr*   r*   r+   __init__^   s&    





zDocumentSecurityStore.__init__c                 C   s   | j S N)rA   r(   r*   r*   r+   modifiedy   s    zDocumentSecurityStore.modifiedc                 C   s(   | j s$d| _ | jd k	r$| j| j d S )NT)rA   r<   r:   Zupdate_containerrF   r*   r*   r+   _mark_modified}   s    
z$DocumentSecurityStore._mark_modifiedc              	   c   sn   |D ]d}|  }z|| V  W q tk
rf   | jtj|d}|   |||< || |V  Y qX qd S )NZstream_data)dumpKeyErrorr:   
add_objectr   StreamObjectrH   append)r(   objsseendestobjZ	obj_bytesrefr*   r*   r+   _cms_objects_to_streams   s    

z-DocumentSecurityStore._cms_objects_to_streamsc                    s     fdd}fdd| D S )Nc                  3   s    D ]} t | E d H  qd S rE   )r8   )respr!   r*   r+   extra_certs   s    zADocumentSecurityStore._embed_certs_from_ocsp.<locals>.extra_certsc                    s   g | ]}  |qS r*   _embed_cert).0Zcert_rF   r*   r+   
<listcomp>   s     z@DocumentSecurityStore._embed_certs_from_ocsp.<locals>.<listcomp>r*   )r(   r!   rW   r*   )r!   r(   r+   _embed_certs_from_ocsp   s    z,DocumentSecurityStore._embed_certs_from_ocspc                 C   sf   | j d krtdz| j|j W S  tk
r4   Y nX | j tj| d}| 	  || j|j< |S )N"This DSS does not support updates.rI   )
r:   	TypeErrorr    issuer_serialrK   rL   r   rM   rJ   rH   )r(   certrS   r*   r*   r+   rY      s    
z!DocumentSecurityStore._embed_certr#   c                 C   s"   t |    }td| S )a  
        Hash the contents of a signature object to get the corresponding VRI
        identifier.

        This is internal API.

        :param contents:
            Signature contents.
        :return:
            A name object to put into the DSS.
        /)hashlibsha1digesthexupperr   )contentsidentr*   r*   r+   sig_content_identifier   s    z,DocumentSecurityStore.sig_content_identifierr*   r    r!   r"   c          	         s    j dkrtdt|}t|}t }t } fdd|D }|rZt | j j}|rtt | j j}|	t 
| |dk	rt|||d} j |  j|<    dS )a  
        Register validation information for a set of signing certificates
        associated with a particular signature.

        :param identifier:
            Identifier of the signature object (see `sig_content_identifier`).
            If ``None``, only embed the data into the DSS without associating
            it with any VRI.
        :param certs:
            Certificates to add.
        :param ocsps:
            OCSP responses to add.
        :param crls:
            CRLs to add.
        Nr]   c                    s   h | ]}  |qS r*   rX   )rZ   r`   rF   r*   r+   	<setcomp>   s     z5DocumentSecurityStore.register_vri.<locals>.<setcomp>rj   )r:   r^   listr2   rT   r?   r!   r@   r"   updater\   r   rL   r,   r;   rH   )	r(   
identifierr    r!   r"   	ocsp_refscrl_refs	cert_refsr)   r*   rF   r+   register_vri   s4    
  
z"DocumentSecurityStore.register_vric                 C   sl   | j }tt| j |d< | jr4t| j|d< | jrNt| j|t	d< | j
rht| j
|t	d< |S )z
        Convert the :class:`.DocumentSecurityStore` object to a python
        dictionary. This method also handles DSS updates.

        :return:
            A PDF object representing this DSS.
        /Certsr%   /OCSPs/CRLs)r<   r   r'   rl   r    valuesr;   r&   r!   r   r"   )r(   Zpdf_dictr*   r*   r+   r,      s    z#DocumentSecurityStore.as_pdf_objectc                 c   s.   | j  D ]}| }t|j}|V  q
dS )z
        Return a generator that parses and yields all certificates in the DSS.

        :return:
            A generator yielding :class:`.Certificate` objects.
        N)r    rv   r=   r   loadr>   )r(   cert_refcert_streamr`   r*   r*   r+   
load_certs   s    z DocumentSecurityStore.load_certsTc                 C   s   t |}|dg }t|  | }|rt|dd}| jD ]$}| }tj|j	}|
| q>||d< t|dd}	| jD ]$}
|
 }tj|j	}|	
| q|	|d< tf d|i|S )ag  
        Construct a validation context from the data in this DSS.

        :param validation_context_kwargs:
            Extra kwargs to pass to the ``__init__`` function.
        :param include_revinfo:
            If ``False``, revocation info is skipped.
        :return:
            A validation context preloaded with information from this DSS.
        other_certsr!   r*   r"   )dictpoprl   rz   r!   r=   	asn1_ocspOCSPResponserw   r>   rN   r"   asn1_crlCertificateListr	   )r(   Zvalidation_context_kwargsZinclude_revinforW   r    r!   rB   ocsp_streamrU   r"   rC   
crl_streamr   r*   r*   r+   as_validation_context
  s*    

z+DocumentSecurityStore.as_validation_context)handlerr$   c              
   C   sL  z|j d }W n* tk
r8 } zt |W 5 d}~X Y nX i }t|dtg d}|D ]"}| }t|j}|||j	< qRt|dtg d}	g }
|	D ]$}| }t
j|j}|
| qt|dtg d}g }|D ]$}| }tj|j}|| qzt|d }W n tk
r   d}Y nX t|tr0|}nd}| |||	|||d}|S )	a  
        Read a DSS record from a file and add the data to a validation context.

        :param handler:
            PDF handler from which to read the DSS.
        :return:
            A DocumentSecurityStore object describing the current state of the
            DSS.
        /DSSNrs   )defaultrt   ru   r%   )r:   r    r!   r;   r"   r<   )rootrK   r   r   rl   r=   r   rw   r>   r_   r~   r   rN   r   r   r|   
isinstancer   )clsr   dss_dicterq   Zcert_ref_listrx   ry   r`   ro   r!   rB   r   rU   rp   r"   rC   r   r   r;   r:   dssr*   r*   r+   read_dss.  sL    
    zDocumentSecurityStore.read_dssr    r!   r"   pathsvalidation_contextembed_roots)pdf_outr   r$   c                   s   z|  |}	d}
W n" tk
r4   d}
| |d}	Y nX |dk	rJt|}nd} fdd}fdd}fd	d
}|	j|| | | d |	 }|
r||}||jtd< |	  |	S )aD  
        Add or update a DSS, and optionally associate the new information with a
        VRI entry tied to a signature object.

        You can either specify the CMS objects to include directly, or
        pass them in as output from `pyhanko_certvalidator`.

        :param pdf_out:
            PDF writer to write to.
        :param sig_contents:
            Contents of the new signature (used to compute the VRI hash), as
            a hexadecimal string, including any padding.
            If ``None``, the information will not be added to any VRI
            dictionary.
        :param certs:
            Certificates to include in the VRI entry.
        :param ocsps:
            OCSP responses to include in the VRI entry.
        :param crls:
            CRLs to include in the VRI entry.
        :param paths:
            Validation paths that have been established, and need to be added
            to the DSS.
        :param validation_context:
            Validation context from which to draw OCSP responses and CRLs.
        :param embed_roots:
            .. versionadded:: 0.9.0

            Option that controls whether the root certificate of each validation
            path should be embedded into the DSS. The default is ``True``.

            .. note::
                Trust roots are configured by the validator, so embedding them
                typically does nothing in a typical validation process.
                Therefore they can be safely omitted in most cases.
                Nonetheless, embedding the roots can be useful for documentation
                purposes.

            .. warning::
                This only applies to paths, not the ``certs`` parameter.

        :return:
            a :class:`DocumentSecurityStore` object containing both the new
            and existing contents of the DSS (if any).
        FTr9   Nc                  3   s>    pdE d H  pdD ]"} t | }s.t| |E d H  qd S Nr*   )iternext)path
path_parts)r    r   r   r*   r+   _certs  s    z:DocumentSecurityStore.supply_dss_in_writer.<locals>._certsc                   3   s&    pdE d H  d k	r"j E d H  d S r   rV   r*   )r!   r   r*   r+   _ocsps  s    z:DocumentSecurityStore.supply_dss_in_writer.<locals>._ocspsc                   3   s&    pdE d H  d k	r"j E d H  d S r   )r"   r*   )r"   r   r*   r+   _crls  s    z9DocumentSecurityStore.supply_dss_in_writer.<locals>._crlsrj   r   )
r   r   r   ri   rr   r,   rL   r   r   Zupdate_root)r   r   sig_contentsr    r!   r"   r   r   r   r   createdrn   r   r   r   r   Zdss_refr*   )r    r"   r   r!   r   r   r+   supply_dss_in_writerh  s2    4

   
z*DocumentSecurityStore.supply_dss_in_writerF)r    r!   r"   r   r   force_writer   file_credential)r   r   r   c             
   C   sV   t |}|jdk	r&|
dk	r&|j|
 | j||||||||	d}|sJ|jrR|  dS )a$  
        Wrapper around :meth:`supply_dss_in_writer`.

        The result is applied to the output stream as an incremental update.

        :param output_stream:
            Output stream to write to.
        :param sig_contents:
            Contents of the new signature (used to compute the VRI hash), as
            a hexadecimal string, including any padding.
            If ``None``, the information will not be added to any VRI
            dictionary.
        :param certs:
            Certificates to include in the VRI entry.
        :param ocsps:
            OCSP responses to include in the VRI entry.
        :param crls:
            CRLs to include in the VRI entry.
        :param paths:
            Validation paths that have been established, and need to be added
            to the DSS.
        :param force_write:
            Force a write even if the DSS doesn't have any new content.
        :param validation_context:
            Validation context from which to draw OCSP responses and CRLs.
        :param embed_roots:
            .. versionadded:: 0.9.0

            Option that controls whether the root certificate of each validation
            path should be embedded into the DSS. The default is ``True``.

            .. note::
                Trust roots are configured by the validator, so embedding them
                typically does nothing in a typical validation process.
                Therefore they can be safely omitted in most cases.
                Nonetheless, embedding the roots can be useful for documentation
                purposes.

            .. warning::
                This only applies to paths, not the ``certs`` parameter.
        :param file_credential:
            .. versionadded:: 0.13.0

            Serialised file credential, to update encrypted files.
        Nr   )r   Zsecurity_handlerZauthenticater   rG   write_in_place)r   Zoutput_streamr   r    r!   r"   r   r   r   r   r   r   r   r*   r*   r+   add_dss  s    2     
zDocumentSecurityStore.add_dss)NNNNN)T)r-   r.   r/   r0   r   rD   propertyrG   rH   rT   r\   rY   staticmethodr   Z
NameObjectri   rr   r,   rz   r	   r   classmethodr   r   boolr   r   r   r   r*   r*   r*   r+   r   Y   sb           
1 $9  a     F)embedded_sigr   c                    s\   j j}|jstd g   fdd}|| jI dH  |sX| jdk	rX|| jI dH   S )a  
    Query revocation info for a PDF signature using a validation context,
    and store the results in a validation context.

    This works by validating the signer's certificate against the provided
    validation context, which causes revocation info to be cached for
    later retrieval.

    .. warning::
        This function does *not* actually validate the signature, but merely
        checks the signer certificate's chain of trust.

    :param embedded_sig:
        Embedded PDF signature to operate on.
    :param validation_context:
        Validation context to use.
    :param skip_timestamp:
        If the signature has a time stamp token attached to it, also collect
        revocation information for the timestamp.
    :return:
        A list of validation paths.
    zfRevocation mode is set to soft-fail/tolerant mode; collected revocation information may be incomplete.c                    sD   t | }|j}|j}t||d}|jt dI d H } | d S )N)Zintermediate_certsr   )Z	key_usage)r   Zsigner_certr{   r   Zasync_validate_usager2   rN   )signed_dataZ	cert_infor`   r{   Z	validatorr   r   r   r*   r+   _validate_signed_data,  s     z6collect_validation_info.<locals>._validate_signed_dataN)Zrevinfo_policyZrevocation_checking_policyZ	essentialloggerwarningr   Zattached_timestamp_data)r   r   skip_timestampZrevinfo_fetch_policyr   r*   r   r+   r     s    T)r   r   r   c	                    s   | j }	|r |	j }
}t| n
t|}
t| ||dI dH }|rT| j d}nd}t	
|	}||_tj|||||d}|s|jr|r|  q||
 n$|s|	jd tt||	j|
 t||
S )aY  
    .. versionadded: 0.9.0

    Add validation info (CRLs, OCSP responses, extra certificates) for a
    signature to the DSS of a document in an incremental update.
    This is a wrapper around :func:`collect_validation_info`.

    :param embedded_sig:
        The signature for which the revocation information needs to be
        collected.
    :param validation_context:
        The validation context to use.
    :param skip_timestamp:
        If ``True``, do not attempt to validate the timestamp attached to
        the signature, if one is present.
    :param add_vri_entry:
        Add a ``/VRI`` entry for this signature to the document security store.
        Default is ``True``.
    :param output:
        Write the output to the specified output stream.
        If ``None``, write to a new :class:`.BytesIO` object.
        Default is ``None``.
    :param in_place:
        Sign the original input stream in-place.
        This parameter overrides ``output``.
    :param chunk_size:
        Chunk size parameter to use when copying output to a new stream
        (irrelevant if ``in_place`` is ``True``).
    :param force_write:
        Force a new revision to be written, even if not necessary (i.e.
        when all data in the validation context is already present in the DSS).
    :param embed_roots:
        Option that controls whether the root certificate of each validation
        path should be embedded into the DSS. The default is ``True``.

        .. note::
            Trust roots are configured by the validator, so embedding them
            typically does nothing in a typical validation process.
            Therefore they can be safely omitted in most cases.
            Nonetheless, embedding the roots can be useful for documentation
            purposes.
    :return:
        The (file-like) output object to which the result was written.
    )r   Nascii)r   r   r   r   )readerstreamr   Z!assert_writable_and_random_accessZprepare_rw_output_streamr   Zpkcs7_contentre   encoder   Zfrom_readerZIO_CHUNK_SIZEr   r   rG   r   writeseekZchunked_write	bytearrayZfinalise_output)r   r   r   Zadd_vri_entryZin_placeoutputr   
chunk_sizer   r   Zworking_outputr   r   r   Zresulting_dssr*   r*   r+   r   ?  s<    4

  
   

)F)7rb   loggingZdataclassesr   r   r1   typingr   Z
asn1cryptor   r   r   r~   Zasn1crypto.x509r   Zpyhanko_certvalidatorr   r	   Zpyhanko_certvalidator.pathr
   Zpyhanko.pdf_utilsr   r   Zpyhanko.pdf_utils.genericr   Z$pyhanko.pdf_utils.incremental_writerr   Zpyhanko.pdf_utils.miscr   Zpyhanko.pdf_utils.rw_commonr   Zpyhanko.pdf_utils.writerr   Zgeneralr   errorsr   r   Zpdf_embeddedr   __all__Zpdf_utils.cryptr   Zpdf_utils.readerr   	getLoggerr-   r   r   r8   r   r   ZDEFAULT_CHUNK_SIZEr   r   r*   r*   r*   r+   <module>   s`     
(   4 9  