U
    gB                     @   sj  d Z ddlmZ ddlmZ ddlmZ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mZmZmZmZmZmZmZmZ dd
lmZ ddlmZmZ ddlm Z  dddddgZ!edddZ"eej#dddZ$d$eee dddZ%eddG dd dZ&eddG d d dZ'eddG d!d dZ(eddG d"d dZ)G d#d dZ*dS )%z
This module describes and implements the low-level :class:`.PdfCMSEmbedder`
protocol for embedding CMS payloads into PDF signature objects.
    )	dataclass)datetime)IOOptional)genericmisc)pdf_name)BoxConstraints)BasePdfFileWriter)	FieldMDPSpecMDPPermSigFieldSpecannot_width_heightapply_sig_field_spec_propertiesensure_sig_flagsenumerate_sig_fieldsget_sig_field_annotprepare_sig_field)SigningError)BaseStampStyleTextStampStyle   )PdfSignedDataPdfCMSEmbedderSigMDPSetupSigObjSetupSigAppearanceSetup
SigIOSetupZpermission_levelc                 C   sX   t tdtdtdtdtdt tdtdtdtdtdt | jiiS )	N/Type/SigRef/TransformMethod/DocMDP/TransformParams/Vz/1.2/P)r   DictionaryObjectr   NumberObjectvaluer    r)   E/tmp/pip-unpacked-wheel-owvgwkas/pyhanko/sign/signers/cms_embedder.pydocmdp_reference_dictionary'   s&          r+   )field_mdp_specdata_refc                 C   sJ   t |j|j|j}t tdtdtdtdtd|td|  iS )Nr   r    r!   z	/FieldMDPz/Datar#   )r   ZIndirectObjectZidnumZ
generationZpdfr&   r   Zas_transform_params)r,   r-   Zdata_ind_objr)   r)   r*   fieldmdp_reference_dictionary:   s           r.   N)pdf_outnew_field_specc                 C   s  |j }| d kr|stdd}t|dd}zt|\}}}	W n tk
rX   tdY nX ddd |D }
|
rtd||
f nl|d k	r|j||jd	 |j	|j
|jd
}ni }t| |f||d|\}}	|r|d k	rt||	 |d t|dd ||	fS )NzJNot specifying a field name is only allowed when existing_fields_only=TrueF)Zfilled_statusz$There are no empty signature fields.z, c                 s   s    | ]\}}}|d k	r|V  qd S Nr)   ).0fn_r)   r)   r*   	<genexpr>g   s      z*_get_or_create_sigfield.<locals>.<genexpr>z^There are several empty signature fields. Please specify a field name. The options are %s, %s.r   )boxZinclude_on_pagecombine_annotationZinvis_settingsZvisible_settings)Zupdate_writerexisting_fields_only)	sig_fieldZsig_field_specT)writerZlock_sig_flags)rootr   r   nextStopIterationjoinr6   Zfind_page_for_modificationZon_pager7   Zinvis_sig_settingsZvisible_sig_settingsr   r   
get_objectr   )
field_namer/   r8   r0   r;   field_createdZempty_fieldsZfound_field_namer4   sig_field_refZothersZsig_field_kwargsr)   r)   r*   _get_or_create_sigfieldN   s`    	

rC   T)frozenc                   @   sJ   e Zd ZU eed< dZeed< dZee	 ed< dZ
ee ed< dd ZdS )	r   md_algorithmFcertifyN
field_lockdocmdp_permsc           
      C   s   | j }| j}| j}t }|r|dk	s*t|j}z|d }W n$ tk
r`   t  |d< }Y nX ||t	d< |
| |t| |dk	rt||jd}	||	 |dk	rt|j|	d d< |r|| d< dS )zy
        Apply the settings to a signature object.

        .. danger::
            This method is internal API.
        Nz/Permsr"   )r-   r#   r%   z
/Reference)rF   rH   rG   r   ZArrayObjectAssertionErrorr;   KeyErrorr&   r   Zupdate_containerappendr+   r.   Zroot_refr'   r(   r?   )
selfsig_obj_refr:   rF   rH   lockZreference_arrayr;   ZpermsZfieldmdp_refr)   r)   r*   apply   s4    
 
zSigMDPSetup.apply)__name__
__module____qualname__str__annotations__rF   boolrG   r   r   rH   r   rO   r)   r)   r)   r*   r      s
   
c                   @   sN   e Zd ZU dZeed< eed< ee ed< dZ	ee
 ed< dd Zd	d
 ZdS )r   z
    Signature appearance configuration.

    Part of the low-level :class:`.PdfCMSEmbedder` API, see
    :class:`SigObjSetup`.
    style	timestampnameNtext_paramsc                 C   s`   t |\}}|r\|r\| |t||d}|  |d< z|td= W n tk
rZ   Y nX dS )zt
        Apply the settings to an annotation.

        .. danger::
            This method is internal API.
        )widthheightz/APz/ASN)r   _appearance_stampr	   Zas_appearancesZas_pdf_objectr   rJ   )rL   	sig_annotr:   whZstampr)   r)   r*   rO      s     
zSigAppearanceSetup.applyc                 C   s^   | j }| j}| j}i }|d k	r&||d< || jp2i  t|trP||j|d< |	|||S )NZsignerts)
rV   rX   rW   updaterY   
isinstancer   strftimeZtimestamp_formatZcreate_stamp)rL   r:   r6   rV   rX   rW   rY   r)   r)   r*   r\     s    
z$SigAppearanceSetup._appearance_stamp)rP   rQ   rR   __doc__r   rT   r   r   rS   rY   dictrO   r\   r)   r)   r)   r*   r      s   
c                   @   s:   e Zd ZU dZeed< dZee ed< dZ	ee
 ed< dS )r   zV
    Describes the signature dictionary to be embedded as the form field's value.
    sig_placeholderN	mdp_setupappearance_setup)rP   rQ   rR   rd   r   rT   rg   r   r   rh   r   r)   r)   r)   r*   r   !  s
   
	c                   @   sD   e Zd ZU dZeed< dZeed< ej	Z
eed< dZee ed< dS )r   z
    I/O settings for writing signed PDF documents.

    Objects of this type are used in the penultimate phase of
    the :class:`.PdfCMSEmbedder` protocol.
    rE   Fin_place
chunk_sizeNoutput)rP   rQ   rR   rd   rS   rT   ri   rU   r   ZDEFAULT_CHUNK_SIZErj   intrk   r   r   r)   r)   r)   r*   r   ;  s   
c                   @   s:   e Zd ZdZd
ee dddZdee eddd	Z	dS )r   ab  
    Low-level class that handles embedding CMS objects into PDF signature
    fields.

    It also takes care of appearance generation and DocMDP configuration,
    but does not otherwise offer any of the conveniences of
    :class:`.PdfSigner`.

    :param new_field_spec:
        :class:`.SigFieldSpec` to use when creating new fields on-the-fly.
    Nr0   c                 C   s
   || _ d S r1   rm   )rL   r0   r)   r)   r*   __init__o  s    zPdfCMSEmbedder.__init__F)r@   r:   c                 c   s   |s
| j nd}t||||d\}}|V }t|ts6t| }|j}	|	dk	r`t|}
|	|
| |j	}|
|}||td< |s|| |j}|dk	r||| |V }t|tst|j||j|j|j|jdE dH  dS )a  
        .. versionadded:: 0.3.0

        .. versionchanged:: 0.7.0
            Digest wrapped in
            :class:`~pyhanko.sign.signers.pdf_byterange.PreparedByteRangeDigest`
            in step 3; ``output`` returned in step 3 instead of step 4.

        This method returns a generator coroutine that controls the process
        of embedding CMS data into a PDF signature field.
        Can be used for both timestamps and regular signatures.

        .. danger::
            This is a very low-level interface that performs virtually no
            error checking, and is intended to be used in situations
            where the construction of the CMS object to be embedded
            is not under the caller's control (e.g. a remote signer
            that produces full-fledged CMS objects).

            In almost every other case, you're better of using
            :class:`.PdfSigner` instead, with a custom :class:`.Signer`
            implementation to handle the cryptographic operations if necessary.

        The coroutine follows the following specific protocol.

        1. First, it retrieves or creates the signature field to embed the
           CMS object in, and yields a reference to said field.
        2. The caller should then send in a :class:`.SigObjSetup` object, which
           is subsequently processed by the coroutine. For convenience, the
           coroutine will then yield a reference to the signature dictionary
           (as embedded in the PDF writer).
        3. Next, the caller should send a :class:`.SigIOSetup` object,
           describing how the resulting document should be hashed and written
           to the output. The coroutine will write the entire document with a
           placeholder region reserved for the signature and compute the
           document's hash and yield it to the caller.
           It will then yield a ``prepared_digest, output`` tuple, where
           ``prepared_digest`` is a :class:`.PreparedByteRangeDigest` object
           containing the document digest and the relevant offsets, and
           ``output`` is the output stream to which the document to be
           signed was written.

           From this point onwards, **no objects may be changed or added** to
           the :class:`.IncrementalPdfFileWriter` currently in use.
        4. Finally, the caller should pass in a CMS object to place inside
           the signature dictionary. The CMS object can be supplied as a raw
           :class:`bytes` object, or an :mod:`asn1crypto`-style object.
           The coroutine's final yield is the value of the signature
           dictionary's ``/Contents`` entry, given as a hexadecimal string.

        .. caution::
            It is the caller's own responsibility to ensure that enough room
            is available in the placeholder signature object to contain
            the final CMS object.

        :param field_name:
            The name of the field to fill in. This should be a field of type
            ``/Sig``.
        :param writer:
            An :class:`.IncrementalPdfFileWriter` containing the
            document to sign.
        :param existing_fields_only:
            If ``True``, never create a new empty signature field to contain
            the signature.
            If ``False``, a new field may be created if no field matching
            ``field_name`` exists.
        :return:
            A generator coroutine implementing the protocol described above.
        Nrm   r$   )ri   rk   rj   )r0   rC   rb   r   rI   r?   rh   r   rO   rf   
add_objectr   Zmark_updaterg   r   fillrE   ri   rk   rj   )rL   r@   r:   r8   r0   rA   rB   Zsig_obj_setupr9   rh   r]   Zsig_objrM   rg   Zsig_ior)   r)   r*   	write_cmsr  s@    M
	

zPdfCMSEmbedder.write_cms)N)F)
rP   rQ   rR   rd   r   r   rn   rS   r
   rq   r)   r)   r)   r*   r   b  s    )N)+rd   Zdataclassesr   r   typingr   r   Zpyhanko.pdf_utilsr   r   Zpyhanko.pdf_utils.genericr   Zpyhanko.pdf_utils.layoutr	   Zpyhanko.pdf_utils.writerr
   Zpyhanko.sign.fieldsr   r   r   r   r   r   r   r   r   Zpyhanko.sign.generalr   Zpyhanko.stampr   r   Zpdf_byteranger   __all__r+   Z	Referencer.   rC   r   r   r   r   r   r)   r)   r)   r*   <module>   sF   ,	  BLC&