Re: TLS 1.3 and SNI; test script included

Charles Cazabon <[email protected]>
Newsgroups gmane.mail.getmail.user
Message-ID <[email protected]>
[email protected] <[email protected]> wrote:
> 
> Wodan> ./test-imap.py imap.gmail.com <http://imap.gmail.com/>
> Traceback (most recent call last):
>   File "./test-imap.py", line 11, in <module>
>     ssl_version=ssl.PROTOCOL_TLS, ca_certs=None,
> AttributeError: 'module' object has no attribute 'PROTOCOL_TLS'

Indeed, that value isn't present before 2.7.13.  If you care to try, here's an
updated version that may fail for different reasons ;)

Charles

-- 
-----------------------------------------------------------------------
Charles Cazabon
GPL'ed software available at:               http://pyropus.ca/software/
-----------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
test-imap.py (text/x-python, 3.4 KB)
#!/usr/bin/env python2.7

import sys
import imaplib
import socket
import ssl
import hashlib

def wrap_socket(sock, keyfile=None, certfile=None,
                server_side=False, cert_reqs=ssl.CERT_NONE,
                ssl_version=ssl.PROTOCOL_TLS, ca_certs=None,
                do_handshake_on_connect=True,
                suppress_ragged_eofs=True,
                ciphers=None, server_hostname=None):
    return ssl.SSLSocket(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, server_hostname=server_hostname)

ssl.wrap_socket = wrap_socket

PROTO_BEST = getattr(ssl, 'PROTOCOL_TLS', None)


#######################################
class IMAP4_SSL_EXTENDED(imaplib.IMAP4_SSL):
    # Similar to above, but with extended support for SSL certificate checking,
    # fingerprints, etc.
    def __init__(self, host, port=imaplib.IMAP4_SSL_PORT,
                 ssl_version=None, use_sni=False):
        self.ssl_version = ssl_version
        self.use_sni = use_sni
        imaplib.IMAP4_SSL.__init__(self, host, port, keyfile=None, certfile=None)

    def open(self, host='', port=imaplib.IMAP4_SSL_PORT):
        self.host = host
        self.port = port
        self.sock = socket.create_connection((host, port))
        extra_args = {}
        if self.ssl_version:
            extra_args['ssl_version'] = self.ssl_version
        if self.use_sni:
            extra_args['server_hostname'] = self.host

        self.sslobj = ssl.wrap_socket(self.sock, self.keyfile, self.certfile, 
                                     **extra_args)
        self.file = self.sslobj.makefile('rb')


has_sni = getattr(ssl, 'HAS_SNI', None)
if has_sni is None:
    raise SystemExit('no SNI support')
if not has_sni:
    raise SystemExit('OpenSSL - no SNI support')
has_tls13 = getattr(ssl, 'HAS_TLSv1_3', None)
if has_tls13 is None:
    raise SystemExit('no TLS1.3 support')
if not has_tls13:
    raise SystemExit('OpenSSL - no TLS1.3 support')

server = sys.argv[1]
port = imaplib.IMAP4_SSL_PORT
if len(sys.argv) == 3:
    port = int(sys.argv[2])

print 'Connecting to %s:%d' % (server, port)

certhash_nosni = None
certhash_sni = None
try:
    conn = IMAP4_SSL_EXTENDED(
        server, port, ssl_version=PROTO_BEST, use_sni=False
    )
    sslobj = conn.ssl()
    peercert = sslobj.getpeercert(True)
    certhash_nosni = hashlib.sha256(peercert).hexdigest()
    ssl_cipher = sslobj.cipher()
    print 'No SNI - got cipher %s' % str(ssl_cipher)
except Exception as o:
    sys.stderr.write('TLS 1.3, no SNI - connection failed (%s)\n' % o)
else:
    conn.logout()

try:
    conn = IMAP4_SSL_EXTENDED(
        server, port, ssl_version=PROTO_BEST, use_sni=True
    )
    sslobj = conn.ssl()
    peercert = sslobj.getpeercert(True)
    certhash_sni = hashlib.sha256(peercert).hexdigest()
    ssl_cipher = sslobj.cipher()
    print 'SNI - got cipher %s' % str(ssl_cipher)
except Exception as o:
    sys.stderr.write('TLS 1.3, with SNI - connection failed (%s)\n' % o)
else:
    conn.logout()

if certhash_nosni and certhash_sni and certhash_nosni != certhash_sni:
    sys.stderr.write('Different certs for no-SNI and SNI: %s vs %s\n' 
                     % (certhash_nosni, certhash_sni))
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.