U
    clX                     @   s2  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
mZ ddl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	lmZ dd
lmZ ddlmZm Z  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(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.d0d Z/d1d Z0ej1ej2d2d3d"Z3d^ej1ej4d2d5d#Z5ej1eej6ej7f d6d7d%Z8eej9ej:f ej1e;d8d9d$Z<G d:d de'Z=G d;d de=Z>d_eej?ef d=d>dZ@eeAd?d@dZBd`ejCdAdBd!ZDej1eAejCdCdDd ZEdEd ZFeGdFdGdZHdHd ZIe	eG ejJdIdJdZKeGe	eG ejJdKdLdZLejJd=dMdNZMej1d=dOdPZNedQdRG dSd dZOejPdTdUdVZQdWdX ZRejSejTdYdZdZUejSeOdYd[dZVe"jWfeeeX eAe
eXeGf d\d]d'ZYdS )aa  
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OptionalTupleUnion)algoscmskeyspemtspx509)SignedDigestAlgorithm)hashesserialization)padding)RSAPublicKey)	Prehashed)CertificateStoreSimpleCertificateStore)miscsimple_cms_attributefind_cms_attributefind_unique_cms_attributeNonexistentAttributeErrorMultivaluedAttributeErrorr   r   SigningErrorUnacceptableSignerErrorSignedDataCertsextract_signer_infoextract_certificate_infoload_certs_from_pemderload_cert_from_pemderload_private_key_from_pemderload_certs_from_pemder_data!load_private_key_from_pemder_dataget_cms_hash_algo_for_mechanismget_pyca_cryptography_hashoptimal_pss_paramsprocess_pss_paramsas_signing_certificateas_signing_certificate_v2match_issuer_serialcheck_ess_certidCMSExtractionErrorbyte_range_digestValueErrorWithMessageCMSStructuralErrorc                   @   s   e Zd ZdZdd ZdS )r1   z
    Value error with a failure message attribute that can be conveniently
    extracted, instead of having to rely on extracting exception args
    generically.
    c                 C   s   t || _d S N)strfailure_message)selfr5    r7   8/tmp/pip-unpacked-wheel-0kb_yl26/pyhanko/sign/general.py__init__5   s    zValueErrorWithMessage.__init__N)__name__
__module____qualname____doc__r9   r7   r7   r7   r8   r1   /   s   c                   @   s   e Zd ZdZdS )r2   z!Structural error in a CMS object.Nr:   r;   r<   r=   r7   r7   r7   r8   r2   9   s   c                   @   s   e Zd ZdS )r/   Nr:   r;   r<   r7   r7   r7   r8   r/   =   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valuer7   r7   r8   r   A   s    c                   @   s   e Zd ZdS )r   Nr?   r7   r7   r7   r8   r   S   s   c                   @   s   e Zd ZdS )r   Nr?   r7   r7   r7   r8   r   W   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 duplicatedrA   zUnable to locate attribute .)nativer2   r   )attrsnameZfound_valuesattrr7   r7   r8   r   [   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   )rE   rF   rA   r7   r7   r8   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issuerrO   )	cert_hashissuer_serial)
r   SigningCertificate	ESSCertIDhashlibsha1dumpdigestr   GeneralNamerQ   rJ   r7   r7   r8   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.
    rL   	algorithmrM   rN   rO   rP   )hash_algorithmrR   rS   )r(   r   HashupdaterX   finalizer   SigningCertificateV2ESSCertIDv2r   rZ   rQ   )rJ   	hash_algo	hash_specmddigest_valuer7   r7   r8   r,      s     

)rJ   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.
    rW   r^   r]   rR   FrS   )
isinstancer   rU   rD   r(   r   r_   r`   rX   ra   r-   )rJ   rh   rd   re   rf   rg   Zexpected_digest_valueexpected_issuer_serialr7   r7   r8   r.      s    

)rj   rJ   rK   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.
    rN   rO   rQ   rH   r   rM   F)	ri   r   ZGeneralNamesrI   rF   chosenrX   rQ   
ValueError)rj   rJ   Zserial_asn1Zexpected_issuerZissuer_matchr7   r7   r8   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 r3   )rm   superr9   )r6   rm   args	__class__r7   r8   r9   /  s    zSigningError.__init__)r:   r;   r<   r=   r4   r9   __classcell__r7   r7   rp   r8   r   +  s   c                   @   s   e Zd ZdZdS )r   z=
    Error raised when a signer was judged unacceptable.
    Nr>   r7   r7   r7   r8   r   4  s   F)rK   c                 C   s:   |   dkrtjdd}ntt|   }|r6t|S |S )Nshake256@   )digest_size)lowerr   ZSHAKE256getattrupperr   )r]   	prehashedrd   r7   r7   r8   r(   ;  s    )mechrK   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.
    Zed25519sha512Zed448rs   N)Zsignature_algord   )rz   Zsig_algor7   r7   r8   r'   E  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|dd}t||d}	tjtj	|d|d}
|
|	fS )zs
    Extract PSS padding settings and message digest from an
    ``RSASSAPSSParams`` value.

    Internal API.
    r^   r]   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_lengthF)ry   )r]   )Zmgfr   )
rD   casefoldrl   NotImplementedErrorloggerwarningr(   r   ZPSSZMGF1)r|   digest_algorithmry   rd   Zmd_nameZmgaZmgf_md_nameZsalt_lenZmgf_mdrf   Zpss_paddingr7   r7   r8   r*   \  s,    	


)rJ   r   rK   c              
   C   s^   |  }t| j }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.
    r]   r~   )r]   r   )r^   r}   r   )rv   r   Zload_der_public_keyZ
public_keyrX   r(   r   Zcalculate_max_pss_salt_lengthr	   RSASSAPSSParamsZDigestAlgorithmZMaskGenAlgorithm)rJ   r   keyrf   Zoptimal_salt_lenr7   r7   r8   r)     s(      c              
   c   s:   | D ]0}t |d}| }W 5 Q R X t|E dH  qdS )z
    A convenience function to load PEM/DER-encoded certificates from files.

    :param cert_files:
        An iterable of file names.
    :return:
        A generator producing :class:`.asn1crypto.x509.Certificate` objects.
    rbN)openreadr%   )Z
cert_files	cert_filefcert_data_bytesr7   r7   r8   r"     s    	)r   c                 c   sr   t | r`t j| dd}|D ]@\}}}|dks:| dkrJtj|V  qtd| d qntj| V  dS )a  
    A convenience function to load PEM/DER-encoded certificates from
    binary data.

    :param cert_data_bytes:
        ``bytes`` object from which to extract certificates.
    :return:
        A generator producing :class:`.asn1crypto.x509.Certificate` objects.
    T)multipleNcertificatezSkipping PEM block of type z in CA chain file.)	r   detectZunarmorrv   r   Certificateloadr   debug)r   Zpems	type_name_Zderr7   r7   r8   r%     s    

c                 C   s2   t t| g}t|dkr*td|  d|d S )z
    A convenience function to load a single PEM/DER-encoded certificate
    from a file.

    :param cert_file:
        A file name.
    :return:
        An :class:`.asn1crypto.x509.Certificate` object.
    rH   zNumber of certs in z should be exactly 1r   )listr"   rI   rl   )r   rL   r7   r7   r8   r#     s    

)
passphraserK   c              	   C   s*   t | d}| }W 5 Q R X t||dS )a  
    A convenience function to load PEM/DER-encoded keys from files.

    :param key_file:
        File to read the key from.
    :param passphrase:
        Key passphrase.
    :return:
        A private key encoded as an unencrypted PKCS#8 PrivateKeyInfo object.
    r   )r   )r   r   r&   )key_filer   r   	key_bytesr7   r7   r8   r$     s    )r   r   rK   c                 C   s&   t | rtjntj}t|| |dS )a  
    A convenience function to load PEM/DER-encoded keys from binary data.

    :param key_bytes:
        ``bytes`` object to read the key from.
    :param passphrase:
        Key passphrase.
    :return:
        A private key encoded as an unencrypted PKCS#8 PrivateKeyInfo object.
    )password)r   r   r   Zload_pem_private_keyZload_der_private_key(_translate_pyca_cryptography_key_to_asn1)r   r   Zload_funr7   r7   r8   r&     s    
c                 C   s"   t j| tjjtjjt	 S r3   )
r   PrivateKeyInfor   Zprivate_bytesr   EncodingDERZPrivateFormatZPKCS8ZNoEncryption)Zprivate_keyr7   r7   r8   r     s     r   c                 C   s   t j| tjjS r3   )r   r   r   Zpublic_bytesr   r   r   r[   r7   r7   r8   )_translate_pyca_cryptography_cert_to_asn1   s    r   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)
r:   r;   r<   r=   r   r   __annotations__r   r
   ZAttributeCertificateV2r7   r7   r7   r8   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 r3   )r-   rk   cr   r7   r8   <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 r3   )Zkey_identifierr   )skir7   r8   r   H  r   )rF   rk   rD   r   r   r   r   r7   )r   r   r8   _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/   )rL   signer_info	predicaterJ   r   r   r7   r7   r8   _partition_certsL  s    r   )signed_datarK   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)rl   r/   )r   r   r7   r7   r8   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.
    Zcertificatesr   Zv2_attr_cert)r   r   r   )rk   ZuntagrF   r   r    r   r   )	r   rL   Z
attr_certsr   rJ   r   r   r   Z	cert_infor7   r7   r8   r!   s  s     



 )stream
byte_rangemd_algorithmrK   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   r_   	bytearrayr   Z	pair_iterseekZchunked_digestra   )
r   r   r   
chunk_sizeZmd_specrf   Z	total_lenZ	chunk_bufloZ	chunk_lenr7   r7   r8   r0     s    


)r\   )F)F)Zr=   rV   loggingZdataclassesr   typingr   r   r   r   r   r   Z
asn1cryptor	   r
   r   r   r   r   Zasn1crypto.algosr   Zcryptography.hazmat.primitivesr   r   Z)cryptography.hazmat.primitives.asymmetricr   Z-cryptography.hazmat.primitives.asymmetric.rsar   Z/cryptography.hazmat.primitives.asymmetric.utilsr   Zpyhanko_certvalidator.registryr   r   Zpyhanko.pdf_utilsr   __all__	getLoggerr:   r   rl   r1   r2   r/   r   KeyErrorr   r   r   r   r   rT   r+   rb   r,   rU   rc   r.   ZIssuerAndSerialNumberZIssuerSerialboolr-   r   r   ZHashAlgorithmr(   r4   r'   r   r*   r)   r"   bytesr%   r#   r   r$   r&   r   r   r   ZSignerIdentifierr   r   Z
SignedDataZ
SignerInfor    r!   ZDEFAULT_CHUNK_SIZEintr0   r7   r7   r7   r8   <module>   s                    

&%! 1	
 '% 

