o
    vhVK                     @   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 g d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*dd Z+dej,dej-fd d!Z.	"dPdej,dej/fd#d$Z0dej,d%e
ej1ej2f fd&d'Z3d(e
ej4ej5f dej,de6fd)d*Z7G d+d, d,e"Z8G d-d. d.e8Z9de
ej: fd/d0Z;	1dQde
ej:ef fd2d3Z<d4ede=fd5d6Z>	1dQd7ej?fd8d9Z@dej,d:e=dej?fd;d<ZAed=d>G d?d@ d@ZBdAejCfdBdCZDdDdE ZEdFejFdejGfdGdHZHdFejFdeBfdIdJZIejJfdKedLeeK dMe=de	eKeLf fdNdOZMdS )Ra  
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CMSStructuralErrorload_cert_from_pemderload_certs_from_pemderload_certs_from_pemder_dataload_private_key_from_pemder!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__)selfr2   	__class__ R/var/www/html/hyperkenya/venv/lib/python3.10/site-packages/pyhanko/sign/general.pyr4   A   s   
zValueErrorWithMessage.__init__)__name__
__module____qualname____doc__r4   __classcell__r8   r8   r6   r9   r)   :   s    r)   c                   @      e Zd ZdZdS )r*   z!Structural error in a CMS object.Nr:   r;   r<   r=   r8   r8   r8   r9   r*   F   s    r*   c                   @      e Zd ZdS )r'   Nr:   r;   r<   r8   r8   r8   r9   r'   J       r'   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	   CMSAttributeCMSAttributeType)	attr_typevaluer8   r8   r9   r   N   s   r   c                   @   rA   )r   NrB   r8   r8   r8   r9   r   _   rC   r   c                   @   rA   )r   NrB   r8   r8   r8   r9   r   c   rC   r   c                 C   s\   d}| r | D ]}|d j |kr|durtd|d|d }q|dur&|S td| d)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.
    NrD   z
Attribute z was duplicatedrE   zUnable to locate attribute .)nativer*   r   )attrsnamefound_valuesattrr8   r8   r9   r   g   s   
r   c                 C   s8   t | |}t|dkrt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   )rL   rM   rE   r8   r8   r9   r      s   
r   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issuerrW   )	cert_hashissuer_serial)
r
   SigningCertificate	ESSCertIDhashlibsha1dumpdigestr   GeneralNamerY   )rR   r8   r8   r9   r#      s"   r#   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.
    rT   	algorithmrU   rV   rW   rX   )hash_algorithmrZ   r[   )r   r   Hashupdater`   finalizer
   SigningCertificateV2ESSCertIDv2r   rb   rY   )rR   	hash_algo	hash_specmddigest_valuer8   r8   r9   r$      s,   
r$   certidc                 C   sv   t |tjr	d}n|d d j}t|}t|}||   |	 }|d j}||kr/dS |d }| p:t
|| 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.
    r_   re   rd   rZ   Fr[   )
isinstancer
   r]   rK   r   r   rf   rg   r`   rh   r%   )rR   ro   rk   rl   rm   rn   expected_digest_valueexpected_issuer_serialr8   r8   r9   r&      s   

r&   rr   c                 C   s   |d d }| d }t |tjr$t|dks|d jdkrdS |d j}z| |j kp2||jk}W n ty?   d}Y nw |oG| 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.
    rV   rW   rY   rP   r   rU   F)	rp   r   GeneralNamesrQ   rM   chosenr`   rY   
ValueError)rr   rR   serial_asn1expected_issuerissuer_matchr8   r8   r9   r%     s    
r%   c                       s&   e Zd ZdZdef fddZ  ZS )r   z1
    Error encountered while signing a file.
    msgc                    s   || _ t j|g|R   d S r0   )ry   r3   r4   )r5   ry   argsr6   r8   r9   r4   P  s   zSigningError.__init__)r:   r;   r<   r=   r1   r4   r>   r8   r8   r6   r9   r   K  s    r   c                   @   r?   )r   z=
    Error raised when a signer was judged unacceptable.
    Nr@   r8   r8   r8   r9   r   U  s    r   c                 C   s(   |   dv rtjddS tt|   S )N)shake256shake256_len@   )digest_size)lowerr   SHAKE256getattrupperrd   r8   r8   r9   r   ]  s   r   Fc                 C   s   t | }|r
t|S |S r0   )r   r   )rd   	prehashedrk   r8   r8   r9   r    f  s   r    mechc                 C   s$   | j }|dkr	dS |dkrdS | j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ed448r{   )signature_algork   )r   sig_algor8   r8   r9   r   m  s   r   paramsc                 C   s   | d }|d j }| | krtd| d| d| d }|d j dks+td|d	 d j }||krBt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.
    re   rd   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)r   r   )mgfr   )rK   casefoldru   NotImplementedErrorloggerwarningr   r    r   PSSMGF1)r   digest_algorithmr   rk   md_namemgamgf_md_namesalt_lenmgf_mdrm   pss_paddingr8   r8   r9   r"     s2   


r"   r   c              
   C   sz   |  }t| j }t|tst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 rd   r   )rd   r   )re   r   r   )r   r   load_der_public_key
public_keyr`   rp   r   r   rD   r   r   calculate_max_pss_salt_lengthr   RSASSAPSSParamsDigestAlgorithmMaskGenAlgorithm)rR   r   keyrm   optimal_salt_lenr8   r8   r9   r!     s(   
r!   T)frozenc                   @   s<   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   Certificate__annotations__r   r	   AttributeCertificateV2r8   r8   r8   r9   r     s   
 
r   sidc                    sB    j dkr fddS  j dkr jjtd fddS t)Nissuer_and_serial_numberc                    s   t  j| S r0   )r%   rt   cr   r8   r9   <lambda>  s    z'_get_signer_predicate.<locals>.<lambda>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 r0   )key_identifierr   )skir8   r9   r     s   
 )rM   rt   rK   r   r   r   r   r8   )r   r   r9   _get_signer_predicate  s   

r   c                 C   sN   t |d }d }g }| D ]}||r|}q|| q|d u r#td||fS )Nr   z,signer certificate not included in signature)r   appendr'   )rT   signer_info	predicaterR   r   r   r8   r8   r9   _partition_certs  s   r   signed_datac                 C   s(   z| d \}|W S  t y   tdw )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.
    signer_infosz-signer_infos should contain exactly one entry)ru   r'   )r   r   r8   r8   r9   r     s   
r   c           	      C   sr   g }g }| d D ]}|j  }|jdkr|| 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certificatev2_attr_cert)r   r   r   )rt   untagrM   r   r   r   r   )	r   rT   
attr_certsr   rR   r   r   r   	cert_infor8   r8   r9   r   !  s"   




r   stream
byte_rangemd_algorithmc           
      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   )max_read)	r   r   rf   	bytearrayr   	pair_iterseekchunked_digestrh   )
r   r   r   
chunk_sizemd_specrm   	total_len	chunk_buflo	chunk_lenr8   r8   r9   r(   >  s   


r(   )rc   )F)Nr=   r^   loggingdataclassesr   typingr   r   r   r   r   
asn1cryptor   r	   r
   r   asn1crypto.algosr   cryptography.hazmat.primitivesr   r   )cryptography.hazmat.primitives.asymmetricr   -cryptography.hazmat.primitives.asymmetric.rsar   /cryptography.hazmat.primitives.asymmetric.utilsr   pyhanko.keyspyhanko.pdf_utilsr   __all__	getLoggerr:   r   ru   r)   r*   r'   r   KeyErrorr   r   r   r   r   r\   r#   ri   r$   r]   rj   r&   IssuerAndSerialNumberIssuerSerialboolr%   r   r   HashAlgorithmr   r    r1   r   r   r"   r!   r   SignerIdentifierr   r   
SignedData
SignerInfor   r   DEFAULT_CHUNK_SIZEintbytesr(   r8   r8   r8   r9   <module>   s    
 &$
.
 
4



'
)!
