U
    ]c?                     @   s  d Z ddlZddlZddlZddlZ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 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  ddl!m"Z" e#e$Z%dEddZ&dd Z'dd Z(dd Z)dd Z*dd Z+dd  Z,d!d" Z-d#d$ Z.d%d& Z/d'd( Z0d)d* Z1d+d, Z2e
j3fd-d.Z4e
j3fd/d0Z5e
j3fd1d2Z6d3d4 Z7d5d6 Z8e
j3fd7d8Z9e
j3fd9d:Z:d;d< Z;d=d> Z<d?d@ Z=dAdB Z>dCdD Z?dS )FzCertbot client crypto utility functions.

.. todo:: Make the transition to use PSS rather than PKCS1_v1_5 when the server
    is capable of handling the signatures.

    N)SSL)crypto)x509)InvalidSignature)default_backend)ECDSA)EllipticCurvePublicKey)PKCS1v15)RSAPublicKey)crypto_util)IO)errors)
interfaces)util)oskey-certbot.pemc              
   C   s   zt | }W n4 tk
r@ } ztjddd |W 5 d}~X Y nX tjtj}t	
|d|j t	tj||dd\}}| || W 5 Q R X td| | t	||S )	a  Initializes and saves a privkey.

    Inits key and saves it in PEM format on the filesystem.

    .. note:: keyname is the attempted filename, it may be different if a file
        already exists at the path.

    :param int key_size: RSA key size in bits
    :param str key_dir: Key save directory.
    :param str keyname: Filename of key

    :returns: Key
    :rtype: :class:`certbot.util.Key`

    :raises ValueError: If unable to generate the key given key_size.

     Texc_infoNi  i  wbzGenerating key (%d bits): %s)make_key
ValueErrorloggererrorzope	component
getUtilityr   IConfigr   make_or_verify_dirstrict_permissionsunique_filer   pathjoinwritedebugZKey)Zkey_sizeZkey_dirZkeynameZkey_pemerrconfigZkey_fkey_path r(   5/usr/lib/python3/dist-packages/certbot/crypto_util.pyinit_save_key%   s       r*   c              	   C   s   t jtj}tj| j||jd}t	
|d|j t	tj|ddd\}}| || W 5 Q R X td| t	||dS )a2  Initialize a CSR with the given private key.

    :param privkey: Key to include in the CSR
    :type privkey: :class:`certbot.util.Key`

    :param set names: `str` names to include in the CSR

    :param str path: Certificate save directory.

    :returns: CSR
    :rtype: :class:`certbot.util.CSR`

    )must_staplei  zcsr-certbot.pemi  r   zCreating CSR: %spem)r   r   r   r   r   acme_crypto_utilZmake_csrr,   r+   r   r   r   r    r   r!   r"   r#   r   r$   CSR)privkeynamesr!   r&   Zcsr_pemZcsr_fZcsr_filenamer(   r(   r)   init_save_csrI   s         r1   c                 C   sJ   zt t j| }|| W S  t jk
rD   tjddd Y dS X dS )zValidate CSR.

    Check if `csr` is a valid CSR for the given domains.

    :param str csr: CSR in PEM.

    :returns: Validity of CSR.
    :rtype: bool

    r   Tr   FN)r   load_certificate_requestFILETYPE_PEMverifyZ
get_pubkeyErrorr   r$   )csrreqr(   r(   r)   	valid_csrl   s     r8   c                 C   sT   t t j| }t t j|}z||W S  t jk
rN   tjddd Y dS X dS )zDoes private key correspond to the subject public key in the CSR?

    :param str csr: CSR in PEM.
    :param str privkey: Private key file contents (PEM)

    :returns: Correspondence of private key to CSR subject public key.
    :rtype: bool

    r   Tr   FN)r   r2   r3   load_privatekeyr4   r5   r   r$   )r6   r/   r7   Zpkeyr(   r(   r)   csr_matches_pubkey   s    
 r:   c                 C   s   t j}t j}z|t j|}W nL t jk
rh   z|||}W n& t jk
rb   td| Y nX Y nX t|}t ||}|t	j
| |dd|fS )a/  Import a CSR file, which can be either PEM or DER.

    :param str csrfile: CSR filename
    :param str data: contents of the CSR file

    :returns: (`crypto.FILETYPE_PEM`,
               util.CSR object representing the CSR,
               list of domains requested in the CSR)
    :rtype: tuple

    zFailed to parse CSR file: {0}r,   )filedataZform)r   r3   r2   FILETYPE_ASN1r5   r   format"_get_names_from_loaded_cert_or_reqZdump_certificate_requestr   r.   )Zcsrfiler<   ZPEMloadr6   ZdomainsZdata_pemr(   r(   r)   import_csr_file   s    rA   c                 C   s0   | dkst t }|tj|  ttj|S )zGenerate PEM encoded RSA key.

    :param int bits: Number of bits, at least 1024.

    :returns: new RSA key in PEM form with specified number of bits
    :rtype: str

    i   )AssertionErrorr   ZPKeyZgenerate_keyZTYPE_RSAZdump_privatekeyr3   )bitskeyr(   r(   r)   r      s    	r   c              	   C   s6   zt t j|  W S  tt jfk
r0   Y dS X dS )zIs valid RSA private key?

    :param str privkey: Private key file contents in PEM

    :returns: Validity of private key.
    :rtype: bool

    FN)r   r9   r3   Zcheck	TypeErrorr5   )r/   r(   r(   r)   valid_privkey   s    	 
rF   c                 C   s"   t |  t|  t| j| j dS )a  For checking that your certs were not corrupted on disk.

    Several things are checked:
        1. Signature verification for the cert.
        2. That fullchain matches cert and chain when concatenated.
        3. Check that the private key matches the certificate.

    :param `.storage.RenewableCert` renewable_cert: cert to verify

    :raises errors.Error: If verification fails.
    N)verify_renewable_cert_sigverify_fullchainverify_cert_matches_priv_keycertr/   )renewable_certr(   r(   r)   verify_renewable_cert   s    rL   c              
   C   s   zt | jd}t| t }W 5 Q R X t | jd}t| t }W 5 Q R X | }t	  t
||j|j|j W 5 Q R X W nJ tttfk
r } z&d| j|}t| t|W 5 d}~X Y nX dS )zVerifies the signature of a `.storage.RenewableCert` object.

    :param `.storage.RenewableCert` renewable_cert: cert to verify

    :raises errors.Error: If signature verification fails.
    rbz[verifying the signature of the cert located at {0} has failed.                 Details: {1}N)openchainr   Zload_pem_x509_certificatereadr   rJ   
public_keywarningscatch_warningsverify_signed_payload	signatureZtbs_certificate_bytessignature_hash_algorithmIOErrorr   r   r>   r   	exceptionr   r5   )rK   
chain_filerO   	cert_filerJ   Zpke	error_strr(   r(   r)   rG      s"    
 
rG   c              	   C   s   t  v t d t| trB| |t |}|| |  n8t| t	rp| |t
|}|| |  n
tdW 5 Q R X dS )a  Check the signature of a payload.

    :param RSAPublicKey/EllipticCurvePublicKey public_key: the public_key to check signature
    :param bytes signature: the signature bytes
    :param bytes payload: the payload bytes
    :param cryptography.hazmat.primitives.hashes.HashAlgorithm
           signature_hash_algorithm: algorithm used to hash the payload

    :raises InvalidSignature: If signature verification fails.
    :raises errors.Error: If public key type is not supported
    ignorezUnsupported public key typeN)rR   rS   simplefilter
isinstancer
   verifierr	   updater4   r   r   r   r5   )rQ   rU   ZpayloadrV   r`   r(   r(   r)   rT      s$    


  


 

rT   c              
   C   s|   z,t t j}||  || |  W nJ tt jfk
rv } z&d| ||}t	
| t|W 5 d}~X Y nX dS )z Verifies that the private key and cert match.

    :param str cert_path: path to a cert in PEM format
    :param str key_path: path to a private key file

    :raises errors.Error: If they don't match.
    zverifying the cert located at {0} matches the                 private key located at {1} has failed.                 Details: {2}N)r   ZContextZSSLv23_METHODZuse_certificate_fileZuse_privatekey_fileZcheck_privatekeyrW   r5   r>   r   rX   r   )	cert_pathr'   contextr[   r\   r(   r(   r)   rI     s    

 
rI   c           	   
   C   s   zt | j}| }W 5 Q R X t | j}| }W 5 Q R X t | j}| }W 5 Q R X || |krd}|| j}t|W nf t	k
r } z"d|}t
| t|W 5 d}~X Y n( tjk
r } z|W 5 d}~X Y nX dS )z Verifies that fullchain is indeed cert concatenated with chain.

    :param `.storage.RenewableCert` renewable_cert: cert to verify

    :raises errors.Error: If cert and chain do not combine to fullchain.
    z.fullchain does not match cert + chain for {0}!z8reading one of cert, chain, or fullchain has failed: {0}N)rN   rO   rP   rJ   	fullchainr>   Zlineagenamer   r5   rW   r   rX   )	rK   rY   rO   rZ   rJ   Zfullchain_filerd   r\   r[   r(   r(   r)   rH   -  s"    

rH   c                 C   s   g }t jt jfD ]J}zt || |fW   S  t jk
rX } z|| W 5 d}~X Y qX qtdddd |D dS )z:Load PEM/DER certificate.

    :raises errors.Error:

    NzUnable to load: {0},c                 s   s   | ]}t |V  qd S N)str).0r   r(   r(   r)   	<genexpr>U  s    z-pyopenssl_load_certificate.<locals>.<genexpr>)	r   r3   r=   load_certificater5   appendr   r>   r"   )r<   Zopenssl_errorsZ	file_typer   r(   r(   r)   pyopenssl_load_certificateG  s    rl   c                 C   s8   z||| W S  t jk
r2   tjddd  Y nX d S )Nr   Tr   )r   r5   r   r   Zcert_or_req_str	load_functypr(   r(   r)   _load_cert_or_reqY  s
    rp   c                 C   s   t t| ||S rf   )r-   Z_pyopenssl_cert_or_req_sanrp   rm   r(   r(   r)   _get_sans_from_cert_or_reqb  s
      rq   c                 C   s   t | tj|S )zGet a list of Subject Alternative Names from a certificate.

    :param str cert: Certificate (encoded).
    :param typ: `crypto.FILETYPE_PEM` or `crypto.FILETYPE_ASN1`

    :returns: A list of Subject Alternative Names.
    :rtype: list

    )rq   r   rj   )rJ   ro   r(   r(   r)   get_sans_from_certi  s
    
  rr   c                 C   s   t | ||}t|S rf   )rp   r?   )Zcert_or_reqrn   ro   loaded_cert_or_reqr(   r(   r)   _get_names_from_cert_or_reqw  s    rt   c                 C   s
   t | S rf   )r-   Z _pyopenssl_cert_or_req_all_names)rs   r(   r(   r)   r?   |  s    r?   c                 C   s   t | tj|S )zGet a list of domains from a cert, including the CN if it is set.

    :param str cert: Certificate (encoded).
    :param typ: `crypto.FILETYPE_PEM` or `crypto.FILETYPE_ASN1`

    :returns: A list of domain names.
    :rtype: list

    )rt   r   rj   )r6   ro   r(   r(   r)   get_names_from_cert  s
    
  ru   c                 C   s   t | |S )zDump certificate chain into a bundle.

    :param list chain: List of `crypto.X509` (or wrapped in
        :class:`josepy.util.ComparableX509`).

    )r-   dump_pyopenssl_chain)rO   Zfiletyper(   r(   r)   rv     s    	rv   c                 C   s   t | tjjS )zWhen does the cert at cert_path start being valid?

    :param str cert_path: path to a cert in PEM format

    :returns: the notBefore value from the cert at cert_path
    :rtype: :class:`datetime.datetime`

    )_notAfterBeforer   X509Zget_notBeforerb   r(   r(   r)   	notBefore  s    	rz   c                 C   s   t | tjjS )zWhen does the cert at cert_path stop being valid?

    :param str cert_path: path to a cert in PEM format

    :returns: the notAfter value from the cert at cert_path
    :rtype: :class:`datetime.datetime`

    )rw   r   rx   Zget_notAfterry   r(   r(   r)   notAfter  s    	r{   c                 C   s   t | }ttj| }W 5 Q R X ||}|dd d|dd d|dd d|dd d|dd	 d|d	d
 g}d|}tjr|d}t	
|S )aP  Internal helper function for finding notbefore/notafter.

    :param str cert_path: path to a cert in PEM format
    :param function method: one of ``crypto.X509.get_notBefore``
        or ``crypto.X509.get_notAfter``

    :returns: the notBefore or notAfter value from the cert at cert_path
    :rtype: :class:`datetime.datetime`

    r         -         T
      :   N    ascii)rN   r   rj   r3   rP   r"   sixZPY3decode	pyrfc3339parse)rb   methodfr   Z	timestampZreformatted_timestampZtimestamp_strr(   r(   r)   rw     s$    

  
 
  


rw   c              	   C   s:   t  }t| d}|| d W 5 Q R X | S )aN  Compute a sha256sum of a file.

    NB: In given file, platform specific newlines characters will be converted
    into their equivalent unicode counterparts before calculating the hash.

    :param str filename: path to the file whose hash will be computed

    :returns: sha256 digest of the file in hexadecimal
    :rtype: str
    rzUTF-8)hashlibsha256rN   ra   rP   encodeZ	hexdigest)filenamer   Zfile_dr(   r(   r)   	sha256sum  s    r   c                 C   s8   t t jt t j|  }| t|d  }||fS )zSplit fullchain_pem into cert_pem and chain_pem

    :param str fullchain_pem: concatenated cert + chain

    :returns: tuple of string cert_pem and chain_pem
    :rtype: tuple

    N)r   Zdump_certificater3   rj   r   lenlstrip)Zfullchain_pemrJ   rO   r(   r(   r)   cert_and_chain_from_fullchain  s
    	r   )r   )@__doc__r   ZloggingrR   r   r   Zzope.componentr   ZOpenSSLr   r   Zcryptographyr   Zcryptography.exceptionsr   Zcryptography.hazmat.backendsr   Z,cryptography.hazmat.primitives.asymmetric.ecr   r   Z1cryptography.hazmat.primitives.asymmetric.paddingr	   Z-cryptography.hazmat.primitives.asymmetric.rsar
   Zacmer   r-   Zacme.magic_typingr   Zcertbotr   r   r   Zcertbot.compatr   Z	getLogger__name__r   r*   r1   r8   r:   rA   r   rF   rL   rG   rT   rI   rH   rl   r3   rp   rq   rr   rt   r?   ru   rv   rz   r{   rw   r   r   r(   r(   r(   r)   <module>   sb   

$#


