U
    ±*Pd†+  ã                   @   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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ejZG d
d„ deƒZddedfdd„Zddd„Zdd„ Zdd„ Zddd„Ze
jfdd„Z dS ) zCrypto utilities.é    N)Úcrypto)ÚSSL)Úerrors)ÚCallable)ÚOptional)ÚTuple)ÚUnionc                   @   sD   e Zd ZdZefdd„Zdd„ Zdd„ ZG dd	„ d	eƒZ	d
d„ Z
dS )Ú	SSLSocketzëSSL wrapper for sockets.

    :ivar socket sock: Original wrapped socket.
    :ivar dict certs: Mapping from domain names (`bytes`) to
        `OpenSSL.crypto.X509`.
    :ivar method: See `OpenSSL.SSL.Context` for allowed values.

    c                 C   s   || _ || _|| _d S ©N)ÚsockÚcertsÚmethod)Úselfr   r   r   © r   ú2/usr/lib/python3/dist-packages/acme/crypto_util.pyÚ__init__(   s    zSSLSocket.__init__c                 C   s   t | j|ƒS r
   )Úgetattrr   ©r   Únamer   r   r   Ú__getattr__-   s    zSSLSocket.__getattr__c                 C   s„   |  ¡ }z| j| \}}W n" tk
r<   t d|¡ Y dS X t | j¡}| tj	¡ | tj
¡ | |¡ | |¡ | |¡ dS )a  SNI certificate callback.

        This method will set a new OpenSSL context object for this
        connection when an incoming connection provides an SNI name
        (in order to serve the appropriate certificate, if any).

        :param connection: The TLS connection object on which the SNI
            extension was received.
        :type connection: :class:`OpenSSL.Connection`

        z-Server name (%s) not recognized, dropping SSLN)Zget_servernamer   ÚKeyErrorÚloggerÚdebugr   ÚContextr   Úset_optionsÚOP_NO_SSLv2ÚOP_NO_SSLv3Zuse_privatekeyZuse_certificateZset_context)r   Ú
connectionZserver_nameÚkeyÚcertZnew_contextr   r   r   Ú_pick_certificate_cb0   s    ÿ

zSSLSocket._pick_certificate_cbc                   @   s(   e Zd ZdZdd„ Zdd„ Zdd„ ZdS )	zSSLSocket.FakeConnectionzFake OpenSSL.SSL.Connection.c                 C   s
   || _ d S r
   )Ú_wrapped)r   r   r   r   r   r   O   s    z!SSLSocket.FakeConnection.__init__c                 C   s   t | j|ƒS r
   )r   r!   r   r   r   r   r   R   s    z$SSLSocket.FakeConnection.__getattr__c                 G   s
   | j  ¡ S r
   )r!   Úshutdown)r   Zunused_argsr   r   r   r"   U   s    z!SSLSocket.FakeConnection.shutdownN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r"   r   r   r   r   ÚFakeConnectionJ   s   r'   c              
   C   s¨   | j  ¡ \}}t | j¡}| tj¡ | tj¡ | | j	¡ |  
t ||¡¡}| ¡  t d|¡ z| ¡  W n. tjk
rž } zt |¡‚W 5 d }~X Y nX ||fS )NzPerforming handshake with %s)r   Úacceptr   r   r   r   r   r   Zset_tlsext_servername_callbackr    r'   Ú
ConnectionZset_accept_stater   r   Údo_handshakeÚErrorÚsocketÚerror)r   r   ZaddrÚcontextZssl_sockr-   r   r   r   r(   Y   s    zSSLSocket.acceptN)r#   r$   r%   r&   Ú_DEFAULT_SSL_METHODr   r   r    Úobjectr'   r(   r   r   r   r   r	      s   r	   i»  i,  )Ú r   c                 C   s  t  |¡}| |¡ d|i}zBt d|||r@d |d |d ¡nd¡ ||f}tj|f|Ž}	W n. tjk
rŒ }
 zt	 
|
¡‚W 5 d}
~
X Y nX t |	¡h}t  ||¡}| ¡  | | ¡ z| ¡  | ¡  W n. t j
k
rú }
 zt	 
|
¡‚W 5 d}
~
X Y nX W 5 Q R X | ¡ S )a  Probe SNI server for SSL certificate.

    :param bytes name: Byte string to send as the server name in the
        client hello message.
    :param bytes host: Host to connect to.
    :param int port: Port to connect to.
    :param int timeout: Timeout in seconds.
    :param method: See `OpenSSL.SSL.Context` for allowed values.
    :param tuple source_address: Enables multi-path probing (selection
        of source interface). See `socket.creation_connection` for more
        info. Available only in Python 2.7+.

    :raises acme.errors.Error: In case of any problems.

    :returns: SSL certificate presented by the server.
    :rtype: OpenSSL.crypto.X509

    Úsource_addressz!Attempting to connect to %s:%d%s.z from {0}:{1}r   é   r1   N)r   r   Zset_timeoutr   r   Úformatr,   Zcreate_connectionr-   r   r+   Ú
contextlibÚclosingr)   Zset_connect_stateZset_tlsext_host_namer*   r"   Zget_peer_certificate)r   ZhostZportZtimeoutr   r2   r.   Zsocket_kwargsZsocket_tupler   r-   ZclientZ
client_sslr   r   r   Ú	probe_snio   s:    

  ýþû
&r7   Fc                 C   s   t  t j| ¡}t  ¡ }t jddd dd„ |D ƒ¡ d¡dg}|rX| t jddd	d¡ | |¡ | 	|¡ | 
d
¡ | |d¡ t  t j|¡S )a©  Generate a CSR containing a list of domains as subjectAltNames.

    :param buffer private_key_pem: Private key, in PEM PKCS#8 format.
    :param list domains: List of DNS names to include in subjectAltNames of CSR.
    :param bool must_staple: Whether to include the TLS Feature extension (aka
        OCSP Must Staple: https://tools.ietf.org/html/rfc7633).
    :returns: buffer PEM-encoded Certificate Signing Request.
    ó   subjectAltNameFú, c                 s   s   | ]}d | V  qdS )zDNS:Nr   ©Ú.0Údr   r   r   Ú	<genexpr>°   s     zmake_csr.<locals>.<genexpr>Úascii©ZcriticalÚvalues   1.3.6.1.5.5.7.1.24s   DER:30:03:02:01:05r   Úsha256)r   Zload_privatekeyÚFILETYPE_PEMZX509ReqÚX509ExtensionÚjoinÚencodeÚappendÚadd_extensionsÚ
set_pubkeyÚset_versionÚsignÚdump_certificate_request)Zprivate_key_pemÚdomainsZmust_stapleZprivate_keyZcsrÚ
extensionsr   r   r   Úmake_csr    s2    	 ÿýÿý


 ÿrN   c                    s6   |   ¡ j‰ t| ƒ}ˆ d kr|S ˆ g‡ fdd„|D ƒ S )Nc                    s   g | ]}|ˆ kr|‘qS r   r   r:   ©Zcommon_namer   r   Ú
<listcomp>Æ   s      z4_pyopenssl_cert_or_req_all_names.<locals>.<listcomp>)Úget_subjectÚCNÚ_pyopenssl_cert_or_req_san)Zloaded_cert_or_reqZsansr   rO   r   Ú _pyopenssl_cert_or_req_all_namesÀ   s
    
rT   c                    sx   d‰ d}dˆ  ‰t | tjƒr$tj}ntj}|tj| ƒ d¡}t d|¡}|dkrTg n| 	d¡ 
|¡}‡ ‡fdd	„|D ƒS )
a¡  Get Subject Alternative Names from certificate or CSR using pyOpenSSL.

    .. todo:: Implement directly in PyOpenSSL!

    .. note:: Although this is `acme` internal API, it is used by
        `letsencrypt`.

    :param cert_or_req: Certificate or CSR.
    :type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.

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

    ú:r9   ZDNSzutf-8z5X509v3 Subject Alternative Name:(?: critical)?\s*(.*)Nr3   c                    s$   g | ]}|  ˆ¡r| ˆ ¡d  ‘qS )r3   )Ú
startswithÚsplit)r;   Úpart©Zpart_separatorÚprefixr   r   rP   ï   s    
ÿz._pyopenssl_cert_or_req_san.<locals>.<listcomp>)Ú
isinstancer   ÚX509Údump_certificaterK   ZFILETYPE_TEXTÚdecodeÚreÚsearchÚgrouprW   )Zcert_or_reqZparts_separatorÚfuncÚtextÚmatchZ
sans_partsr   rY   r   rS   È   s    ÿrS   é€:	 Tc              	   C   sà   |st dƒ‚t ¡ }| tt t d¡¡dƒ¡ | 	d¡ t 
ddd¡g}|d | ¡ _| | ¡ ¡ |svt|ƒdkrœ| tj
d	d
d dd„ |D ƒ¡d¡ | |¡ | |dkr¶dn|¡ | |¡ | | ¡ | | d¡ |S )až  Generate new self-signed certificate.

    :type domains: `list` of `unicode`
    :param OpenSSL.crypto.PKey key:
    :param bool force_san:

    If more than one domain is provided, all of the domains are put into
    ``subjectAltName`` X.509 extension and first domain is set as the
    subject CN. If only one domain is provided no ``subjectAltName``
    extension is used, unless `force_san` is ``True``.

    z0Must provide one or more hostnames for the cert.é   é   s   basicConstraintsTs   CA:TRUE, pathlen:0r   r3   r8   Fs   , c                 s   s   | ]}d |  ¡  V  qdS )s   DNS:N)rE   r:   r   r   r   r=     s     zgen_ss_cert.<locals>.<genexpr>r?   NrA   )ÚAssertionErrorr   r\   Zset_serial_numberÚintÚbinasciiZhexlifyÚosÚurandomrI   rC   rQ   rR   Z
set_issuerÚlenrF   rD   rG   Zgmtime_adj_notBeforeZgmtime_adj_notAfterrH   rJ   )r   rL   Z
not_beforeZvalidityZ	force_sanr   rM   r   r   r   Úgen_ss_certó   s0    
  ÿÿý


rn   c                    s$   ‡fdd„‰ d  ‡ fdd„| D ƒ¡S )zØDump certificate chain into a bundle.

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

    :returns: certificate chain bundle
    :rtype: bytes

    c                    s   t | tjƒr| j} t ˆ | ¡S r
   )r[   ÚjoseZComparableX509Úwrappedr   r]   )r   )Úfiletyper   r   Ú
_dump_cert,  s    z(dump_pyopenssl_chain.<locals>._dump_certó    c                 3   s   | ]}ˆ |ƒV  qd S r
   r   )r;   r   )rr   r   r   r=   4  s     z'dump_pyopenssl_chain.<locals>.<genexpr>)rD   )Úchainrq   r   )rr   rq   r   Údump_pyopenssl_chain  s    ru   )F)Nre   T)!r&   rj   r5   Zloggingrk   r_   r,   Zjosepyro   ZOpenSSLr   r   Zacmer   Zacme.magic_typingr   r   r   r   Z	getLoggerr#   r   ZSSLv23_METHODr/   r0   r	   r7   rN   rT   rS   rn   rB   ru   r   r   r   r   Ú<module>   s:   
	P ÿ
1
 +    ÿ
,