# Wrapper module for _ssl, providing some additional facilities# implemented in Python. Written by Bill Janssen."""This module provides some more Pythonic support for SSL.Object types: SSLSocket -- subtype of socket.socket which does SSL over the socketExceptions: SSLError -- exception raised for I/O errorsFunctions: cert_time_to_seconds -- convert time string used for certificate notBefore and notAfter functions to integer seconds past the Epoch (the time values returned from time.time()) fetch_server_certificate (HOST, PORT) -- fetch the certificate provided by the server running on HOST at port PORT. No validation of the certificate is performed.Integer constants:SSL_ERROR_ZERO_RETURNSSL_ERROR_WANT_READSSL_ERROR_WANT_WRITESSL_ERROR_WANT_X509_LOOKUPSSL_ERROR_SYSCALLSSL_ERROR_SSLSSL_ERROR_WANT_CONNECTSSL_ERROR_EOFSSL_ERROR_INVALID_ERROR_CODEThe following group define certificate requirements that one side isallowing/requiring from the other side:CERT_NONE - no certificates from the other side are required (or will be looked at if provided)CERT_OPTIONAL - certificates are not required, but if provided will be validated, and if validation fails, the connection will also failCERT_REQUIRED - certificates are required, and will be validated, and if validation fails, the connection will also failThe following constants identify various SSL protocol variants:PROTOCOL_SSLv2PROTOCOL_SSLv3PROTOCOL_SSLv23PROTOCOL_TLSv1PROTOCOL_TLSv1_1PROTOCOL_TLSv1_2The following constants identify various SSL alert message descriptions as perhttp://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6ALERT_DESCRIPTION_CLOSE_NOTIFYALERT_DESCRIPTION_UNEXPECTED_MESSAGEALERT_DESCRIPTION_BAD_RECORD_MACALERT_DESCRIPTION_RECORD_OVERFLOWALERT_DESCRIPTION_DECOMPRESSION_FAILUREALERT_DESCRIPTION_HANDSHAKE_FAILUREALERT_DESCRIPTION_BAD_CERTIFICATEALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATEALERT_DESCRIPTION_CERTIFICATE_REVOKEDALERT_DESCRIPTION_CERTIFICATE_EXPIREDALERT_DESCRIPTION_CERTIFICATE_UNKNOWNALERT_DESCRIPTION_ILLEGAL_PARAMETERALERT_DESCRIPTION_UNKNOWN_CAALERT_DESCRIPTION_ACCESS_DENIEDALERT_DESCRIPTION_DECODE_ERRORALERT_DESCRIPTION_DECRYPT_ERRORALERT_DESCRIPTION_PROTOCOL_VERSIONALERT_DESCRIPTION_INSUFFICIENT_SECURITYALERT_DESCRIPTION_INTERNAL_ERRORALERT_DESCRIPTION_USER_CANCELLEDALERT_DESCRIPTION_NO_RENEGOTIATIONALERT_DESCRIPTION_UNSUPPORTED_EXTENSIONALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLEALERT_DESCRIPTION_UNRECOGNIZED_NAMEALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSEALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUEALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY"""importtextwrapimportreimport_ssl# if we can't import it, let the error propagatefrom_sslimportOPENSSL_VERSION_NUMBER,OPENSSL_VERSION_INFO,OPENSSL_VERSIONfrom_sslimport_SSLContextfrom_sslimport(SSLError,SSLZeroReturnError,SSLWantReadError,SSLWantWriteError,SSLSyscallError,SSLEOFError,)from_sslimportCERT_NONE,CERT_OPTIONAL,CERT_REQUIREDfrom_sslimportRAND_status,RAND_egd,RAND_add,RAND_bytes,RAND_pseudo_bytesdef_import_symbols(prefix):fornindir(_ssl):ifn.startswith(prefix):globals()[n]=getattr(_ssl,n)_import_symbols('OP_')_import_symbols('ALERT_DESCRIPTION_')_import_symbols('SSL_ERROR_')from_sslimportHAS_SNI,HAS_ECDH,HAS_NPNfrom_sslimportPROTOCOL_SSLv3,PROTOCOL_SSLv23,PROTOCOL_TLSv1from_sslimport_OPENSSL_API_VERSION_PROTOCOL_NAMES={PROTOCOL_TLSv1:"TLSv1",PROTOCOL_SSLv23:"SSLv23",PROTOCOL_SSLv3:"SSLv3",}try:from_sslimportPROTOCOL_SSLv2_SSLv2_IF_EXISTS=PROTOCOL_SSLv2exceptImportError:_SSLv2_IF_EXISTS=Noneelse:_PROTOCOL_NAMES[PROTOCOL_SSLv2]="SSLv2"try:from_sslimportPROTOCOL_TLSv1_1,PROTOCOL_TLSv1_2exceptImportError:passelse:_PROTOCOL_NAMES[PROTOCOL_TLSv1_1]="TLSv1.1"_PROTOCOL_NAMES[PROTOCOL_TLSv1_2]="TLSv1.2"fromsocketimportgetnameinfoas_getnameinfofromsocketimportsocket,AF_INET,SOCK_STREAM,create_connectionimportbase64# for DER-to-PEM translationimporttracebackimporterrnosocket_error=OSError# keep that public name in module namespaceif_ssl.HAS_TLS_UNIQUE:CHANNEL_BINDING_TYPES=['tls-unique']else:CHANNEL_BINDING_TYPES=[]# Disable weak or insecure ciphers by default# (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL')_DEFAULT_CIPHERS='DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2'classCertificateError(ValueError):passdef_dnsname_to_pat(dn,max_wildcards=1):pats=[]forfragindn.split(r'.'):iffrag.count('*')>max_wildcards:# Issue #17980: avoid denials of service by refusing more# than one wildcard per fragment. A survery of established# policy among SSL implementations showed it to be a# reasonable choice.raiseCertificateError("too many wildcards in certificate DNS name: "+repr(dn))iffrag=='*':# When '*' is a fragment by itself, it matches a non-empty dotless# fragment.pats.append('[^.]+')else:# Otherwise, '*' matches any dotless fragment.frag=re.escape(frag)pats.append(frag.replace(r'\*','[^.]*'))returnre.compile(r'\A'+r'\.'.join(pats)+r'\Z',re.IGNORECASE)defmatch_hostname(cert,hostname):"""Verify that *cert* (in decoded format as returned by SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules are mostly followed, but IP addresses are not accepted for *hostname*. CertificateError is raised on failure. On success, the function returns nothing. """ifnotcert:raiseValueError("empty or no certificate")dnsnames=[]san=cert.get('subjectAltName',())forkey,valueinsan:ifkey=='DNS':if_dnsname_to_pat(value).match(hostname):returndnsnames.append(value)ifnotdnsnames:# The subject is only checked when there is no dNSName entry# in subjectAltNameforsubincert.get('subject',()):forkey,valueinsub:# XXX according to RFC 2818, the most specific Common Name# must be used.ifkey=='commonName':if_dnsname_to_pat(value).match(hostname):returndnsnames.append(value)iflen(dnsnames)>1:raiseCertificateError("hostname %r ""doesn't match either of %s"%(hostname,', '.join(map(repr,dnsnames))))eliflen(dnsnames)==1:raiseCertificateError("hostname %r ""doesn't match %r"%(hostname,dnsnames[0]))else:raiseCertificateError("no appropriate commonName or ""subjectAltName fields were found")classSSLContext(_SSLContext):"""An SSLContext holds various SSL-related configuration options and data, such as certificates and possibly a private key."""__slots__=('protocol','__weakref__')def__new__(cls,protocol,*args,**kwargs):self=_SSLContext.__new__(cls,protocol)ifprotocol!=_SSLv2_IF_EXISTS:self.set_ciphers(_DEFAULT_CIPHERS)returnselfdef__init__(self,protocol):self.protocol=protocoldefwrap_socket(self,sock,server_side=False,do_handshake_on_connect=True,suppress_ragged_eofs=True,server_hostname=None):returnSSLSocket(sock=sock,server_side=server_side,do_handshake_on_connect=do_handshake_on_connect,suppress_ragged_eofs=suppress_ragged_eofs,server_hostname=server_hostname,_context=self)defset_npn_protocols(self,npn_protocols):protos=bytearray()forprotocolinnpn_protocols:b=bytes(protocol,'ascii')iflen(b)==0orlen(b)>255:raiseSSLError('NPN protocols must be 1 to 255 in length')protos.append(len(b))protos.extend(b)self._set_npn_protocols(protos)classSSLSocket(socket):"""This class implements a subtype of socket.socket that wraps the underlying OS socket in an SSL context when necessary, and provides read and write methods over that channel."""def__init__(self,sock=None,keyfile=None,certfile=None,server_side=False,cert_reqs=CERT_NONE,ssl_version=PROTOCOL_SSLv23,ca_certs=None,do_handshake_on_connect=True,family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None,suppress_ragged_eofs=True,npn_protocols=None,ciphers=None,server_hostname=None,_context=None):if_context:self._context=_contextelse:ifserver_sideandnotcertfile:raiseValueError("certfile must be specified for server-side ""operations")ifkeyfileandnotcertfile:raiseValueError("certfile must be specified")ifcertfileandnotkeyfile:keyfile=certfileself._context=SSLContext(ssl_version)self._context.verify_mode=cert_reqsifca_certs:self._context.load_verify_locations(ca_certs)ifcertfile:self._context.load_cert_chain(certfile,keyfile)ifnpn_protocols:self._context.set_npn_protocols(npn_protocols)ifciphers:self._context.set_ciphers(ciphers)self.keyfile=keyfileself.certfile=certfileself.cert_reqs=cert_reqsself.ssl_version=ssl_versionself.ca_certs=ca_certsself.ciphers=ciphersifserver_sideandserver_hostname:raiseValueError("server_hostname can only be specified ""in client mode")self.server_side=server_sideself.server_hostname=server_hostnameself.do_handshake_on_connect=do_handshake_on_connectself.suppress_ragged_eofs=suppress_ragged_eofsifsockisnotNone:socket.__init__(self,family=sock.family,type=sock.type,proto=sock.proto,fileno=sock.fileno())self.settimeout(sock.gettimeout())sock.detach()eliffilenoisnotNone:socket.__init__(self,fileno=fileno)else:socket.__init__(self,family=family,type=type,proto=proto)# See if we are connectedtry:self.getpeername()exceptOSErrorase:ife.errno!=errno.ENOTCONN:raiseconnected=Falseelse:connected=Trueself._closed=Falseself._sslobj=Noneself._connected=connectedifconnected:# create the SSL objecttry:self._sslobj=self._context._wrap_socket(self,server_side,server_hostname)ifdo_handshake_on_connect:timeout=self.gettimeout()iftimeout==0.0:# non-blockingraiseValueError("do_handshake_on_connect should not be specified for non-blocking sockets")self.do_handshake()exceptOSErrorasx:self.close()raisex@propertydefcontext(self):returnself._context@context.setterdefcontext(self,ctx):self._context=ctxself._sslobj.context=ctxdefdup(self):raiseNotImplemented("Can't dup() %s instances"%self.__class__.__name__)def_checkClosed(self,msg=None):# raise an exception here if you wish to check for spurious closespassdef_check_connected(self):ifnotself._connected:# getpeername() will raise ENOTCONN if the socket is really# not connected; note that we can be connected even without# _connected being set, e.g. if connect() first returned# EAGAIN.self.getpeername()defread(self,len=0,buffer=None):"""Read up to LEN bytes and return them. Return zero-length string on EOF."""self._checkClosed()try:ifbufferisnotNone:v=self._sslobj.read(len,buffer)else:v=self._sslobj.read(lenor1024)returnvexceptSSLErrorasx:ifx.args[0]==SSL_ERROR_EOFandself.suppress_ragged_eofs:ifbufferisnotNone:return0else:returnb''else:raisedefwrite(self,data):"""Write DATA to the underlying SSL channel. Returns number of bytes of DATA actually transmitted."""self._checkClosed()returnself._sslobj.write(data)defgetpeercert(self,binary_form=False):"""Returns a formatted version of the data in the certificate provided by the other end of the SSL channel. Return None if no certificate was provided, {} if a certificate was provided, but not validated."""self._checkClosed()self._check_connected()returnself._sslobj.peer_certificate(binary_form)defselected_npn_protocol(self):self._checkClosed()ifnotself._sslobjornot_ssl.HAS_NPN:returnNoneelse:returnself._sslobj.selected_npn_protocol()defcipher(self):self._checkClosed()ifnotself._sslobj:returnNoneelse:returnself._sslobj.cipher()defcompression(self):self._checkClosed()ifnotself._sslobj:returnNoneelse:returnself._sslobj.compression()defsend(self,data,flags=0):self._checkClosed()ifself._sslobj:ifflags!=0:raiseValueError("non-zero flags not allowed in calls to send() on %s"%self.__class__)try:v=self._sslobj.write(data)exceptSSLErrorasx:ifx.args[0]==SSL_ERROR_WANT_READ:return0elifx.args[0]==SSL_ERROR_WANT_WRITE:return0else:raiseelse:returnvelse:returnsocket.send(self,data,flags)defsendto(self,data,flags_or_addr,addr=None):self._checkClosed()ifself._sslobj:raiseValueError("sendto not allowed on instances of %s"%self.__class__)elifaddrisNone:returnsocket.sendto(self,data,flags_or_addr)else:returnsocket.sendto(self,data,flags_or_addr,addr)defsendmsg(self,*args,**kwargs):# Ensure programs don't send data unencrypted if they try to# use this method.raiseNotImplementedError("sendmsg not allowed on instances of %s"%self.__class__)defsendall(self,data,flags=0):self._checkClosed()ifself._sslobj:ifflags!=0:raiseValueError("non-zero flags not allowed in calls to sendall() on %s"%self.__class__)amount=len(data)count=0while(count<amount):v=self.send(data[count:])count+=vreturnamountelse:returnsocket.sendall(self,data,flags)defrecv(self,buflen=1024,flags=0):self._checkClosed()ifself._sslobj:ifflags!=0:raiseValueError("non-zero flags not allowed in calls to recv() on %s"%self.__class__)returnself.read(buflen)else:returnsocket.recv(self,buflen,flags)defrecv_into(self,buffer,nbytes=None,flags=0):self._checkClosed()ifbufferand(nbytesisNone):nbytes=len(buffer)elifnbytesisNone:nbytes=1024ifself._sslobj:ifflags!=0:raiseValueError("non-zero flags not allowed in calls to recv_into() on %s"%self.__class__)returnself.read(nbytes,buffer)else:returnsocket.recv_into(self,buffer,nbytes,flags)defrecvfrom(self,buflen=1024,flags=0):self._checkClosed()ifself._sslobj:raiseValueError("recvfrom not allowed on instances of %s"%self.__class__)else:returnsocket.recvfrom(self,buflen,flags)defrecvfrom_into(self,buffer,nbytes=None,flags=0):self._checkClosed()ifself._sslobj:raiseValueError("recvfrom_into not allowed on instances of %s"%self.__class__)else:returnsocket.recvfrom_into(self,buffer,nbytes,flags)defrecvmsg(self,*args,**kwargs):raiseNotImplementedError("recvmsg not allowed on instances of %s"%self.__class__)defrecvmsg_into(self,*args,**kwargs):raiseNotImplementedError("recvmsg_into not allowed on instances of ""%s"%self.__class__)defpending(self):self._checkClosed()ifself._sslobj:returnself._sslobj.pending()else:return0defshutdown(self,how):self._checkClosed()self._sslobj=Nonesocket.shutdown(self,how)defunwrap(self):ifself._sslobj:s=self._sslobj.shutdown()self._sslobj=Nonereturnselse:raiseValueError("No SSL wrapper around "+str(self))def_real_close(self):self._sslobj=Nonesocket._real_close(self)defdo_handshake(self,block=False):"""Perform a TLS/SSL handshake."""self._check_connected()timeout=self.gettimeout()try:iftimeout==0.0andblock:self.settimeout(None)self._sslobj.do_handshake()finally:self.settimeout(timeout)def_real_connect(self,addr,connect_ex):ifself.server_side:raiseValueError("can't connect in server-side mode")# Here we assume that the socket is client-side, and not# connected at the time of the call. We connect it, then wrap it.ifself._connected:raiseValueError("attempt to connect already-connected SSLSocket!")self._sslobj=self.context._wrap_socket(self,False,self.server_hostname)try:ifconnect_ex:rc=socket.connect_ex(self,addr)else:rc=Nonesocket.connect(self,addr)ifnotrc:self._connected=Trueifself.do_handshake_on_connect:self.do_handshake()returnrcexceptOSError:self._sslobj=Noneraisedefconnect(self,addr):"""Connects to remote ADDR, and then wraps the connection in an SSL channel."""self._real_connect(addr,False)defconnect_ex(self,addr):"""Connects to remote ADDR, and then wraps the connection in an SSL channel."""returnself._real_connect(addr,True)defaccept(self):"""Accepts a new connection from a remote client, and returns a tuple containing that new connection wrapped with a server-side SSL channel, and the address of the remote client."""newsock,addr=socket.accept(self)newsock=self.context.wrap_socket(newsock,do_handshake_on_connect=self.do_handshake_on_connect,suppress_ragged_eofs=self.suppress_ragged_eofs,server_side=True)returnnewsock,addrdefget_channel_binding(self,cb_type="tls-unique"):"""Get channel binding data for current connection. Raise ValueError if the requested `cb_type` is not supported. Return bytes of the data or None if the data is not available (e.g. before the handshake). """ifcb_typenotinCHANNEL_BINDING_TYPES:raiseValueError("Unsupported channel binding type")ifcb_type!="tls-unique":raiseNotImplementedError("{0} channel binding type not implemented".format(cb_type))ifself._sslobjisNone:returnNonereturnself._sslobj.tls_unique_cb()defwrap_socket(sock,keyfile=None,certfile=None,server_side=False,cert_reqs=CERT_NONE,ssl_version=PROTOCOL_SSLv23,ca_certs=None,do_handshake_on_connect=True,suppress_ragged_eofs=True,ciphers=None):returnSSLSocket(sock=sock,keyfile=keyfile,certfile=certfile,server_side=server_side,cert_reqs=cert_reqs,ssl_version=ssl_version,ca_certs=ca_certs,do_handshake_on_connect=do_handshake_on_connect,suppress_ragged_eofs=suppress_ragged_eofs,ciphers=ciphers)# some utility functionsdefcert_time_to_seconds(cert_time):"""Takes a date-time string in standard ASN1_print form ("MON DAY 24HOUR:MINUTE:SEC YEAR TIMEZONE") and return a Python time value in seconds past the epoch."""importtimereturntime.mktime(time.strptime(cert_time,"%b %d %H:%M:%S %Y GMT"))PEM_HEADER="-----BEGIN CERTIFICATE-----"PEM_FOOTER="-----END CERTIFICATE-----"defDER_cert_to_PEM_cert(der_cert_bytes):"""Takes a certificate in binary DER format and returns the PEM version of it as a string."""f=str(base64.standard_b64encode(der_cert_bytes),'ASCII','strict')return(PEM_HEADER+'\n'+textwrap.fill(f,64)+'\n'+PEM_FOOTER+'\n')defPEM_cert_to_DER_cert(pem_cert_string):"""Takes a certificate in ASCII PEM format and returns the DER-encoded version of it as a byte sequence"""ifnotpem_cert_string.startswith(PEM_HEADER):raiseValueError("Invalid PEM encoding; must start with %s"%PEM_HEADER)ifnotpem_cert_string.strip().endswith(PEM_FOOTER):raiseValueError("Invalid PEM encoding; must end with %s"%PEM_FOOTER)d=pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)]returnbase64.decodebytes(d.encode('ASCII','strict'))defget_server_certificate(addr,ssl_version=PROTOCOL_SSLv3,ca_certs=None):"""Retrieve the certificate from the server at the specified address, and return it as a PEM-encoded string. If 'ca_certs' is specified, validate the server cert against it. If 'ssl_version' is specified, use it in the connection attempt."""host,port=addrif(ca_certsisnotNone):cert_reqs=CERT_REQUIREDelse:cert_reqs=CERT_NONEs=create_connection(addr)s=wrap_socket(s,ssl_version=ssl_version,cert_reqs=cert_reqs,ca_certs=ca_certs)dercert=s.getpeercert(True)s.close()returnDER_cert_to_PEM_cert(dercert)defget_protocol_name(protocol_code):return_PROTOCOL_NAMES.get(protocol_code,'<unknown>')