U
    <gWK                     @   s  d 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
 ddl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	lmZ dd
lmZ ddlT ddlmZ 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(gZee Z!G d)d" d"e"Z#G d*d# d#e#Z$G d+d  d e#Z%d,d Z&G d-d de'Z(G d.d de"Z)d/d Z*d0d Z+ej,ej-d1d2dZ.dRej,ej/d1d4dZ0ej,e
ej1ej2f d5d6dZ3e
ej4ej5f ej,e6d7d8dZ7G d9d de"Z8G d:d de8Z9e
ej: d;d<dZ;dSe
ej:ef d;d>dZ<ee=d?d@dZ>dTej?dAdBdZ@ej,e=ej?dCdDdZAedEdFG dGd dZBejCdHdIdJZDdKdL ZEejFejGdMdNdZHejFeBdMdOdZIejJfeeeK e=e	eKeLf dPdQd!ZMdS )Ua  
General tools related to Cryptographic Message Syntax (CMS) signatures,
not necessarily to the extent implemented in the PDF specification.

CMS is defined in :rfc:`5652`. To parse CMS messages, pyHanko relies heavily on
`asn1crypto <https://github.com/wbond/asn1crypto>`_.
    N)	dataclass)IOIterableListTupleUnion)algoscmstspx509)SignedDigestAlgorithm)hashesserialization)padding)RSAPublicKey)	Prehashed)*)miscsimple_cms_attributefind_cms_attributefind_unique_cms_attributeNonexistentAttributeErrorMultivaluedAttributeErrorSigningErrorUnacceptableSignerErrorSignedDataCertsextract_signer_infoextract_certificate_infoget_cms_hash_algo_for_mechanismget_pyca_cryptography_hash&get_pyca_cryptography_hash_for_signingoptimal_pss_paramsprocess_pss_paramsas_signing_certificateas_signing_certificate_v2match_issuer_serialcheck_ess_certidCMSExtractionErrorbyte_range_digestValueErrorWithMessageCMSStructuralErrorZload_cert_from_pemderZload_certs_from_pemderZload_certs_from_pemder_dataZload_private_key_from_pemderZ!load_private_key_from_pemder_datac                       s    e Zd ZdZ fddZ  ZS )r)   z
    Value error with a failure message attribute that can be conveniently
    extracted, instead of having to rely on extracting exception args
    generically.
    c                    s   t || _t | d S N)strfailure_messagesuper__init__)selfr-   	__class__ 8/tmp/pip-unpacked-wheel-w101_d3s/pyhanko/sign/general.pyr/   B   s    
zValueErrorWithMessage.__init__)__name__
__module____qualname____doc__r/   __classcell__r3   r3   r1   r4   r)   ;   s   c                   @   s   e Zd ZdZdS )r*   z!Structural error in a CMS object.Nr5   r6   r7   r8   r3   r3   r3   r4   r*   G   s   c                   @   s   e Zd ZdS )r'   Nr5   r6   r7   r3   r3   r3   r4   r'   K   s   c                 C   s   t t | |fdS )a  
    Convenience method to quickly construct a CMS attribute object with
    one value.

    :param attr_type:
        The attribute type, as a string or OID.
    :param value:
        The value.
    :return:
        A :class:`.cms.CMSAttribute` object.
    )typevalues)r	   ZCMSAttributeZCMSAttributeType)Z	attr_typevaluer3   r3   r4   r   O   s    c                   @   s   e Zd ZdS )r   Nr;   r3   r3   r3   r4   r   `   s   c                   @   s   e Zd ZdS )r   Nr;   r3   r3   r3   r4   r   d   s   c                 C   s`   d}| r@| D ]2}|d j |kr|dk	r6td|d|d }q|dk	rL|S td| ddS )a  
    Find and return CMS attribute values of a given type.

    .. note::
        This function will also check for duplicates, but not in the sense
        of multivalued attributes. In other words: multivalued attributes
        are allowed; listing the same attribute OID more than once is not.

    :param attrs:
        The :class:`.cms.CMSAttributes` object.
    :param name:
        The attribute type as a string (as defined in ``asn1crypto``).
    :return:
        The values associated with the requested type, if present.
    :raise NonexistentAttributeError:
        Raised when no such type entry could be found in the
        :class:`.cms.CMSAttributes` object.
    :raise CMSStructuralError:
        Raised if the given OID occurs more than once.
    Nr<   z
Attribute z was duplicatedr=   zUnable to locate attribute .)nativer*   r   )attrsnameZfound_valuesattrr3   r3   r4   r   h   s    

c                 C   s8   t | |}t|dkr0td| dt| d|d S )a   
    Find and return a unique CMS attribute value of a given type.

    :param attrs:
        The :class:`.cms.CMSAttributes` object.
    :param name:
        The attribute type as a string (as defined in ``asn1crypto``).
    :return:
        The value associated with the requested type, if present.
    :raise NonexistentAttributeError:
        Raised when no such type entry could be found in the
        :class:`.cms.CMSAttributes` object.
    :raise MultivaluedAttributeError:
        Raised when the attribute's cardinality is not 1.
       zExpected single-valued z attribute, but found z valuesr   )r   lenr   )rA   rB   r=   r3   r3   r4   r      s    
)certreturnc              
   C   sF   t dt t|   td| j	ig| d d ddgiS )a  
    Format an ASN.1 ``SigningCertificate`` object, where the certificate
    is identified by its SHA-1 digest.

    :param cert:
        An X.509 certificate.
    :return:
        A :class:`tsp.SigningCertificate` object referring to the original
        certificate.
    certsdirectory_nametbs_certificateserial_numberissuerrK   )	cert_hashissuer_serial)
r
   SigningCertificate	ESSCertIDhashlibsha1dumpdigestr   GeneralNamerM   )rF   r3   r3   r4   r#      s"    sha256c                 C   sf   t |}t|}||   | }tdtd|i|t	
d| jig| d d ddgiS )a  
    Format an ASN.1 ``SigningCertificateV2`` value, where the certificate
    is identified by the hash algorithm specified.

    :param cert:
        An X.509 certificate.
    :param hash_algo:
        Hash algorithm to use to digest the certificate.
        Default is SHA-256.
    :return:
        A :class:`tsp.SigningCertificateV2` object referring to the original
        certificate.
    rH   	algorithmrI   rJ   rK   rL   )hash_algorithmrN   rO   )r   r   HashupdaterT   finalizer
   SigningCertificateV2ESSCertIDv2r   rV   rM   )rF   	hash_algo	hash_specmddigest_valuer3   r3   r4   r$      s,    
)rF   certidc                 C   sv   t |tjrd}n|d d j}t|}t|}||   |	 }|d j}||kr^dS |d }| ptt
|| S )a  
    Match an ``ESSCertID`` value against a certificate.

    :param cert:
        The certificate to match against.
    :param certid:
        The ``ESSCertID`` value.
    :return:
        ``True`` if the ``ESSCertID`` matches the certificate,
        ``False`` otherwise.
    rS   rY   rX   rN   FrO   )
isinstancer
   rQ   r@   r   r   rZ   r[   rT   r\   r%   )rF   rc   r_   r`   ra   rb   Zexpected_digest_valueexpected_issuer_serialr3   r3   r4   r&      s    

 )re   rF   rG   c                 C   s   |d d }| d }t |tjrHt|dks:|d jdkr>dS |d j}z | |j kpd||jk}W n tk
r   d}Y nX |o| d |kS )a~  
    Match the issuer and serial number of an X.509 certificate against some
    expected identifier.

    :param expected_issuer_serial:
        A certificate identifier, either :class:`cms.IssuerAndSerialNumber`
        or :class:`tsp.IssuerSerial`.
    :param cert:
        An :class:`x509.Certificate`.
    :return:
        ``True`` if there's a match, ``False`` otherwise.
    rJ   rK   rM   rD   r   rI   F)	rd   r   GeneralNamesrE   rB   chosenrT   rM   
ValueError)re   rF   Zserial_asn1Zexpected_issuerZissuer_matchr3   r3   r4   r%     s"    


c                       s&   e Zd ZdZed fddZ  ZS )r   z1
    Error encountered while signing a file.
    )msgc                    s   || _ t j|f|  d S r+   )ri   r.   r/   )r0   ri   argsr1   r3   r4   r/   Q  s    zSigningError.__init__)r5   r6   r7   r8   r,   r/   r9   r3   r3   r1   r4   r   L  s   c                   @   s   e Zd ZdZdS )r   z=
    Error raised when a signer was judged unacceptable.
    Nr:   r3   r3   r3   r4   r   V  s   )rG   c                 C   s,   |   dkrtjddS tt|   S d S )N)shake256Zshake256_len@   )digest_size)lowerr   SHAKE256getattrupperrX   r3   r3   r4   r   ^  s    Fc                 C   s   t | }|rt|S |S r+   )r   r   )rX   	prehashedr_   r3   r3   r4   r    g  s    )mechrG   c                 C   s(   | j }|dkrdS |dkrdS | jS dS )a%  
    Internal function that takes a :class:`.SignedDigestAlgorithm` instance
    and returns the name of the digest algorithm that has to be used to compute
    the ``messageDigest`` attribute.

    :param mech:
        A signature mechanism.
    :return:
        A digest algorithm name.
    ed25519sha512ed448rk   N)Zsignature_algor_   )rt   Zsig_algor3   r3   r4   r   n  s    )paramsc                 C   s   | d }|d j }| | kr8td| d| d| d }|d j dksVtd|d	 d j }||krtd
| d| d | d j }t|}t||d}	tj	tj
|d|d}
|
|	fS )zs
    Extract PSS padding settings and message digest from an
    ``RSASSAPSSParams`` value.

    Internal API.
    rY   rX   zPSS MD 'z ' must agree with signature MD 'z'.mask_gen_algorithmmgf1zOnly MFG1 is supported
parameterszMessage digest for MGF1 is z", and the one used for signing is zL. If these do not agree, some software may refuse to validate the signature.salt_length)rs   rr   )mgfr|   )r@   casefoldrh   NotImplementedErrorloggerwarningr   r    r   PSSMGF1)rx   digest_algorithmrs   r_   Zmd_nameZmgaZmgf_md_nameZsalt_lenZmgf_mdra   Zpss_paddingr3   r3   r4   r"     s,    



 )rF   r   rG   c              
   C   sz   |  }t| j }t|ts4tdt| t	|}t
||}ttd|itdtd|id|dS )a"  
    Figure out the optimal RSASSA-PSS parameters for a given certificate.
    The subject's public key must be an RSA key.

    :param cert:
        An RSA X.509 certificate.
    :param digest_algorithm:
        The digest algorithm to use.
    :return:
        RSASSA-PSS parameters.
    z&Expected RSA key, but got key of type rX   rz   )rX   r{   )rY   ry   r|   )rn   r   load_der_public_key
public_keyrT   rd   r   r   r<   r   r   calculate_max_pss_salt_lengthr   RSASSAPSSParamsZDigestAlgorithmZMaskGenAlgorithm)rF   r   keyra   Zoptimal_salt_lenr3   r3   r4   r!     s(    
T)frozenc                   @   s8   e Zd ZU dZejed< eej ed< eej	 ed< dS )r   zT
    Value type to describe certificates included in a CMS signed data payload.
    signer_certother_certsattribute_certsN)
r5   r6   r7   r8   r   Certificate__annotations__r   r	   ZAttributeCertificateV2r3   r3   r3   r4   r     s
   

sidc                    sF    j dkr fddS  j dkr> jjtd fddS td S )NZissuer_and_serial_numberc                    s   t  j| S r+   )r%   rg   cr   r3   r4   <lambda>      z'_get_signer_predicate.<locals>.<lambda>Zsubject_key_identifierzThe signature in this SignedData value seems to be identified by a subject key identifier --- this is legal in CMS, but many PDF viewers and SDKs do not support this feature.c                    s
   | j  kS r+   )key_identifierr   )skir3   r4   r     r   )rB   rg   r@   r   r   r   r   r3   )r   r   r4   _get_signer_predicate  s    

r   c                 C   sN   t |d }d }g }| D ]}||r*|}q|| q|d krFtd||fS )Nr   z,signer certificate not included in signature)r   appendr'   )rH   signer_info	predicaterF   r   r   r3   r3   r4   _partition_certs  s    r   )signed_datarG   c                 C   s2   z| d \}|W S  t k
r,   tdY nX dS )a5  
    Extract the unique ``SignerInfo`` entry of a CMS signed data value, or
    throw a ``ValueError``.

    :param signed_data:
        A CMS ``SignedData`` value.
    :return:
        A CMS ``SignerInfo`` value.
    :raises ValueError:
        If the number of ``SignerInfo`` values is not exactly one.
    Zsigner_infosz-signer_infos should contain exactly one entryN)rh   r'   )r   r   r3   r3   r4   r     s    
c           	      C   sr   g }g }| d D ]8}|j  }|jdkr4|| q|jdkr|| qt| }t||\}}t|||d}|S )a  
    Extract and classify embedded certificates found in the ``certificates``
    field of the signed data value.

    :param signed_data:
        A CMS ``SignedData`` value.
    :return:
        A :class:`SignedDataCerts` object containing the embedded certificates.
    certificatescertificateZv2_attr_cert)r   r   r   )rg   ZuntagrB   r   r   r   r   )	r   rH   Z
attr_certsr   rF   r   r   r   Z	cert_infor3   r3   r4   r   "  s     



)stream
byte_rangemd_algorithmrG   c           
      C   sb   t |}t|}d}t|}t|D ],\}}	| | tj|| ||	d ||	7 }q(|| fS )a  
    Internal API to compute byte range digests. Potentially dangerous if used
    without due caution.

    :param stream:
        Stream over which to compute the digest. Must support seeking and
        reading.
    :param byte_range:
        The byte range, as a list of (offset, length) pairs, flattened.
    :param md_algorithm:
        The message digest algorithm to use.
    :param chunk_size:
        The I/O chunk size to use.
    :return:
        A tuple of the total digested length, and the actual digest.
    r   )Zmax_read)	r   r   rZ   	bytearrayr   Z	pair_iterseekZchunked_digestr\   )
r   r   r   
chunk_sizeZmd_specra   Z	total_lenZ	chunk_bufloZ	chunk_lenr3   r3   r4   r(   ?  s    


)rW   )F)F)Nr8   rR   loggingdataclassesr   typingr   r   r   r   r   Z
asn1cryptor   r	   r
   r   Zasn1crypto.algosr   Zcryptography.hazmat.primitivesr   r   )cryptography.hazmat.primitives.asymmetricr   Z-cryptography.hazmat.primitives.asymmetric.rsar   Z/cryptography.hazmat.primitives.asymmetric.utilsr   Zpyhanko.keysZpyhanko.pdf_utilsr   __all__	getLoggerr5   r   rh   r)   r*   r'   r   KeyErrorr   r   r   r   r   rP   r#   r]   r$   rQ   r^   r&   ZIssuerAndSerialNumberZIssuerSerialboolr%   r   r   HashAlgorithmr   r    r,   r   r   r"   r!   r   ZSignerIdentifierr   r   Z
SignedDataZ
SignerInfor   r   ZDEFAULT_CHUNK_SIZEintbytesr(   r3   r3   r3   r4   <module>   s    
&$ / !4

  ( )!
