예제 #1
0
def _parse_all_cipher_suites() -> Dict[TlsVersionEnum, Set[CipherSuite]]:
    tls_version_to_cipher_suites: Dict[TlsVersionEnum, Set[CipherSuite]] = {}

    for tls_version in [
        TlsVersionEnum.SSL_2_0,
        TlsVersionEnum.SSL_3_0,
        TlsVersionEnum.TLS_1_0,
        TlsVersionEnum.TLS_1_1,
    ]:
        openssl_cipher_strings = _parse_all_cipher_suites_with_legacy_openssl(tls_version)
        tls_version_to_cipher_suites[tls_version] = set()
        for cipher_suite_openssl_name in openssl_cipher_strings:
            cipher_suite_rfc_name = _OPENSSL_TO_RFC_NAMES_MAPPING[tls_version][cipher_suite_openssl_name]
            tls_version_to_cipher_suites[tls_version].add(
                CipherSuite(
                    name=cipher_suite_rfc_name,
                    openssl_name=cipher_suite_openssl_name,
                    is_anonymous=True if "anon" in cipher_suite_rfc_name else False,
                    key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_rfc_name],
                )
            )

    # For TLS 1.2, we have to use both the legacy and modern OpenSSL to cover all cipher suites
    cipher_suites_from_legacy_openssl = _parse_all_cipher_suites_with_legacy_openssl(TlsVersionEnum.TLS_1_2)

    ssl_client_modern = SslClient(ssl_version=OpenSslVersionEnum(TlsVersionEnum.TLS_1_2.value))
    ssl_client_modern.set_cipher_list("ALL:COMPLEMENTOFALL:-PSK:-SRP")
    cipher_suites_from_modern_openssl = set(ssl_client_modern.get_cipher_list())

    # Combine the two sets of cipher suites
    openssl_cipher_strings = cipher_suites_from_legacy_openssl.union(cipher_suites_from_modern_openssl)
    tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_2] = set()
    for cipher_suite_openssl_name in openssl_cipher_strings:
        # Ignore TLS 1.3 cipher suites
        if cipher_suite_openssl_name in _TLS_1_3_CIPHER_SUITES:
            continue

        cipher_suite_rfc_name = _OPENSSL_TO_RFC_NAMES_MAPPING[TlsVersionEnum.TLS_1_2][cipher_suite_openssl_name]
        tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_2].add(
            CipherSuite(
                name=cipher_suite_rfc_name,
                openssl_name=cipher_suite_openssl_name,
                is_anonymous=True if "anon" in cipher_suite_rfc_name else False,
                key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_rfc_name],
            )
        )

    # TLS 1.3 - the list is just hardcoded
    tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_3] = {
        CipherSuite(
            # For TLS 1.3 OpenSSL started using the official names
            name=cipher_suite_name,
            openssl_name=cipher_suite_name,
            is_anonymous=False,
            key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_name],
        )
        for cipher_suite_name in _TLS_1_3_CIPHER_SUITES
    }

    return tls_version_to_cipher_suites
예제 #2
0
 def _init(self, dest='localhost', port=443, timeout=5):
     self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     self.socket.settimeout(timeout)
     self.socket.connect((dest, port))
     self.client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_3,
                             underlying_socket=self.socket,
                             ssl_verify_locations=u'mozilla.pem')
    def process_task(self, server_connectivity_info, plugin_command, options_dict=None):
        if options_dict and "verbose" in options_dict.keys():
            verbose_mode = options_dict["verbose"]
        else:
            verbose_mode = False
        test_protocols = {"SSLv3", "TLSv1"}
        thread_pool = ThreadPool()
        ciphers_list = []
        support_protocol_list = []
        for proto in test_protocols:
            if self.test_protocol_support(PROTOCOL_VERSION[proto], server_connectivity_info):
                ssl_client = SslClient(ssl_version=PROTOCOL_VERSION[proto])
                ssl_client.set_cipher_list(proto)
                ciphers_list = ssl_client.get_cipher_list()
                for cipher in ciphers_list:
                    thread_pool.add_job(
                        (self._test_ciphersuite, (server_connectivity_info, PROTOCOL_VERSION[proto], cipher))
                    )
                support_protocol_list.append(proto)
        thread_pool.start(nb_threads=min(len(ciphers_list), self.MAX_THREADS))

        accept_ciphers = []
        reject_ciphers = []
        if verbose_mode:
            print "  VERBOSE MODE PRINT"
            print "  ------------------"

        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptCipher):
                accept_ciphers.append(cipher_result)
            elif isinstance(cipher_result, RejectCipher):
                reject_ciphers.append(cipher_result)
            else:
                raise ValueError("Unexpected result")
            if verbose_mode:
                cipher_result.print_cipher()

        if verbose_mode:
            print "  ----------------------"
            print "  END VERBOSE MODE PRINT"
            print "  ----------------------"

        for error_job in thread_pool.get_error():
            (_, exception) = error_job
            raise exception

        thread_pool.join()

        support_vulnerable_ciphers_set = self.get_vulnerable_ciphers(accept_ciphers)
        is_vulnerable = True if len(support_vulnerable_ciphers_set) > 0 else False

        return BEASTVulnerabilityTesterResult(
            server_connectivity_info,
            plugin_command,
            options_dict,
            support_vulnerable_ciphers_set,
            is_vulnerable,
            support_protocol_list,
        )
    def send_request(self, ssl_client: SslClient) -> str:
        """Send an HTTP GET to the server and return the HTTP status code.
        """
        try:
            ssl_client.write(HttpRequestGenerator.get_request(self._hostname))

            # Parse the response and print the Location header
            http_response = HttpResponseParser.parse_from_ssl_connection(ssl_client)
            if http_response.version == 9:
                # HTTP 0.9 => Probably not an HTTP response
                result = self.ERR_NOT_HTTP
            else:
                redirect = ''
                if 300 <= http_response.status < 400:
                    redirect_location = http_response.getheader('Location')
                    if redirect_location:
                        # Add redirection URL to the result
                        redirect = f' - {redirect_location}'

                result = self.GET_RESULT_FORMAT.format(http_response.status, http_response.reason, redirect)
        except socket.timeout:
            result = self.ERR_HTTP_TIMEOUT
        except IOError:
            result = self.ERR_GENERIC

        return result
 def send_request(self, ssl_client: SslClient) -> str:
     try:
         ssl_client.write(b'NOOP\r\n')
         result = ssl_client.read(2048).strip().decode('utf-8')
     except socket.timeout:
         result = 'Timeout on SMTP NOOP'
     return result
 def send_request(self, ssl_client: SslClient) -> str:
     try:
         ssl_client.write(b"NOOP\r\n")
         result = ssl_client.read(2048).strip().decode("utf-8")
     except socket.timeout:
         result = "Timeout on SMTP NOOP"
     return result
    def send_request(self, ssl_client: SslClient) -> str:
        """Send an HTTP GET to the server and return the HTTP status code.
        """
        try:
            ssl_client.write(HttpRequestGenerator.get_request(self._hostname))

            # Parse the response and print the Location header
            http_response = HttpResponseParser.parse_from_ssl_connection(
                ssl_client)
            if http_response.version == 9:
                # HTTP 0.9 => Probably not an HTTP response
                result = self.ERR_NOT_HTTP
            else:
                redirect = ""
                if 300 <= http_response.status < 400:
                    redirect_location = http_response.getheader("Location")
                    if redirect_location:
                        # Add redirection URL to the result
                        redirect = f" - {redirect_location}"

                result = self.GET_RESULT_FORMAT.format(http_response.status,
                                                       http_response.reason,
                                                       redirect)
        except socket.timeout:
            result = self.ERR_HTTP_TIMEOUT
        except IOError:
            result = self.ERR_GENERIC

        return result
예제 #8
0
파일: X509_Tests.py 프로젝트: danadam/nassl
    def setUp(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.com", 443))

        ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE)
        ssl_client.do_handshake()
        self.cert = ssl_client.get_peer_certificate()._x509
예제 #9
0
    def setUp(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.com", 443))

        ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE)
        ssl_client.do_handshake()
        self.cert = ssl_client.get_peer_certificate()._x509
예제 #10
0
 def get_cipher_list(self, ssl_protocol):
     """Returns list of cipher suites available for protocol version, saves in parameter ssl_protocol.
 
         Args:
         ssl_protocol (str):.
     """
     ssl_client = SslClient(ssl_version=PROTOCOL_VERSION[ssl_protocol])
     ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL')
     return ssl_client.get_cipher_list()
 def get_cipher_list(self, ssl_protocol):
     """Returns list of cipher suites available for protocol version, saves in parameter ssl_protocol.
 
         Args:
         ssl_protocol (str):.
     """
     ssl_client = SslClient(ssl_version=PROTOCOL_VERSION[ssl_protocol])
     ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL')
     return ssl_client.get_cipher_list()
    def process_task(self,
                     server_connectivity_info,
                     plugin_command,
                     options_dict=None):
        ssl_version = self.SSL_VERSIONS_MAPPING[plugin_command]

        # Get the list of available cipher suites for the given ssl version
        ssl_client = SslClient(ssl_version=ssl_version)
        ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL')
        cipher_list = ssl_client.get_cipher_list()

        # Scan for every available cipher suite
        thread_pool = ThreadPool()
        for cipher in cipher_list:
            thread_pool.add_job(
                (self._test_cipher_suite, (server_connectivity_info,
                                           ssl_version, cipher)))

        # Start processing the jobs; One thread per cipher
        thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS))

        accepted_cipher_list = []
        rejected_cipher_list = []
        errored_cipher_list = []

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptedCipherSuite):
                accepted_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, RejectedCipherSuite):
                rejected_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, ErroredCipherSuite):
                errored_cipher_list.append(cipher_result)
            else:
                raise ValueError('Unexpected result')

        # Store thread pool errors; only something completely unexpected would trigger an error
        for failed_job in thread_pool.get_error():
            (_, exception) = failed_job
            raise exception

        thread_pool.join()

        # Test for the cipher suite preference
        preferred_cipher = self._get_preferred_cipher_suite(
            server_connectivity_info, ssl_version, accepted_cipher_list)

        # Generate the results
        plugin_result = OpenSSLCipherSuitesResult(server_connectivity_info,
                                                  plugin_command, options_dict,
                                                  preferred_cipher,
                                                  accepted_cipher_list,
                                                  rejected_cipher_list,
                                                  errored_cipher_list)
        return plugin_result
예제 #13
0
 def test_tls_1_3(self):
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     sock.settimeout(10)
     sock.connect(('tls13.crypto.mozilla.org', 443))
     ssl_client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_3,
                            underlying_socket=sock,
                            ssl_verify=OpenSslVerifyEnum.NONE)
     self.assertTrue(ssl_client)
     ssl_client.shutdown()
     sock.close()
예제 #14
0
    def test_set_groups_curve_x448(self):
        # Given a server that supports a bunch of curves
        with ModernOpenSslServer(
                cipher="ECDHE-RSA-AES256-SHA",
                groups="X25519:prime256v1:X448:secp384r1:secp192k1") as server:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((server.hostname, server.port))

            # And a client that only supports a specific curve: X448
            ssl_client = SslClient(
                ssl_version=OpenSslVersionEnum.TLSV1_2,
                underlying_socket=sock,
                ssl_verify=OpenSslVerifyEnum.NONE,
            )
            configured_curve = OpenSslEcNidEnum.X448
            ssl_client.set_groups([configured_curve])

            # When the client connects to the server
            try:
                ssl_client.do_handshake()
            finally:
                ssl_client.shutdown()

            # The curve enabled in the client is the one that was used
            dh_info = ssl_client.get_ephemeral_key()
            assert isinstance(dh_info, EcDhEphemeralKeyInfo)
            assert dh_info.curve == configured_curve
            assert dh_info.type == OpenSslEvpPkeyEnum.X448
            assert dh_info.size == 448
            assert len(dh_info.public_bytes) == 56
예제 #15
0
    def test_tls_1_3_write_early_data_fail_when_trying_to_send_more_than_max_early_data(self):
        # Given a server that supports TLS 1.3 and early data
        with ModernOpenSslServer(max_early_data=1) as server:
            # That has a previous TLS 1.3 session with the server
            session = self._create_tls_1_3_session(server.hostname, server.port)
            assert session

            # And the server only accepts 1 byte of early data
            max_early = session.get_max_early_data()
            assert 1 == max_early

            # When creating a new connection
            sock_early_data = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock_early_data.settimeout(5)
            sock_early_data.connect((server.hostname, server.port))

            ssl_client_early_data = SslClient(
                ssl_version=OpenSslVersionEnum.TLSV1_3,
                underlying_socket=sock_early_data,
                ssl_verify=OpenSslVerifyEnum.NONE
            )

            # That re-uses the previous TLS 1.3 session
            ssl_client_early_data.set_session(session)
            assert OpenSslEarlyDataStatusEnum.NOT_SENT == ssl_client_early_data.get_early_data_status()

            # When sending too much early data
            # It fails
            with pytest.raises(OpenSSLError, match='too much early data'):
                ssl_client_early_data.write_early_data(
                    'GET / HTTP/1.1\r\nData: {}\r\n\r\n'.format('*' * max_early).encode('ascii')
                )

            ssl_client_early_data.shutdown()
예제 #16
0
def enable_ecdh_cipher_suites(tls_version: TlsVersionEnum,
                              ssl_client: SslClient) -> None:
    """Set the elliptic curve cipher suites."""
    if tls_version == TlsVersionEnum.TLS_1_3:
        # Cipher suites source: https://tools.ietf.org/html/rfc8446#appendix-B.4
        ssl_client.set_ciphersuites(
            "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:"
            "TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256")
    else:
        # TLSv1.2; cipher suite source: https://www.openssl.org/docs/man1.0.2/man1/ciphers.html
        ssl_client.set_cipher_list("ECDH")
예제 #17
0
    def test(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.com", 443))

        ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE)
        ssl_client.do_handshake()
        self.name_entry = ssl_client.get_peer_certificate()._x509.get_subject_name_entries()[0];

        self.assertIsNotNone(self.name_entry.get_data())
        self.assertIsNotNone(self.name_entry.get_object())
예제 #18
0
    def test(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.com", 443))

        sslClient = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE)
        sslClient.do_handshake()
        self.x509ext = sslClient.get_peer_certificate()._x509.get_extensions()[0]

        self.assertIsNotNone(self.x509ext.get_data())
        self.assertIsNotNone(self.x509ext.get_object())
        self.assertIsNotNone(self.x509ext.get_critical())
예제 #19
0
    def setUp(self):
        # Requires being online :(
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.fr", 443))

        ssl_client = SslClient(ssl_version=SSLV23,
                               sock=sock,
                               ssl_verify=SSL_VERIFY_NONE)
        ssl_client.do_handshake()
        self.ssl_client = ssl_client
        self.cert = ssl_client.get_peer_certificate()
예제 #20
0
    def test(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.com", 443))

        ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE)
        ssl_client.do_handshake()
        self.name_entry = ssl_client.get_peer_certificate(
        )._x509.get_subject_name_entries()[0]

        self.assertIsNotNone(self.name_entry.get_data())
        self.assertIsNotNone(self.name_entry.get_object())
예제 #21
0
    def test(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(('www.google.com', 443))

        sslClient = SslClient(sock=sock, ssl_verify=OpenSslVerifyEnum.NONE)
        sslClient.do_handshake()
        self.x509ext = sslClient.get_peer_certificate()._x509.get_extensions()[0]

        self.assertIsNotNone(self.x509ext.get_data())
        self.assertIsNotNone(self.x509ext.get_object())
        self.assertIsNotNone(self.x509ext.get_critical())
예제 #22
0
    def test_hostname_validation(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("www.google.fr", 443))

        ssl_client = SslClient(ssl_version=SSLV23, sock=sock, ssl_verify=SSL_VERIFY_NONE)
        ssl_client.do_handshake()
        self.ssl_client = ssl_client
        self.cert = ssl_client.get_peer_certificate()

        self.assertEqual(X509_NAME_MATCHES_SAN, self.cert.matches_hostname('www.google.fr'))
        self.assertEqual(X509_NAME_MISMATCH, self.cert.matches_hostname('www.tests.com'))
예제 #23
0
    def test_tls_1_3_write_early_data_fail_when_trying_to_send_more_than_max_early_data(self):
        # Given a server that supports TLS 1.3 and early data
        with ModernOpenSslServer(max_early_data=1) as server:
            # That has a previous TLS 1.3 session with the server
            session = self._create_tls_1_3_session(server.hostname, server.port)
            assert session

            # And the server only accepts 1 byte of early data
            max_early = session.get_max_early_data()
            assert 1 == max_early

            # When creating a new connection
            sock_early_data = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock_early_data.settimeout(5)
            sock_early_data.connect((server.hostname, server.port))

            ssl_client_early_data = SslClient(
                ssl_version=OpenSslVersionEnum.TLSV1_3,
                underlying_socket=sock_early_data,
                ssl_verify=OpenSslVerifyEnum.NONE
            )

            # That re-uses the previous TLS 1.3 session
            ssl_client_early_data.set_session(session)
            assert OpenSslEarlyDataStatusEnum.NOT_SENT == ssl_client_early_data.get_early_data_status()

            # When sending too much early data
            # It fails
            with pytest.raises(OpenSSLError, match='too much early data'):
                ssl_client_early_data.write_early_data(
                    'GET / HTTP/1.1\r\nData: {}\r\n\r\n'.format('*' * max_early).encode('ascii')
                )

            ssl_client_early_data.shutdown()
예제 #24
0
    def test_hostname_validation(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(('www.google.fr', 443))

        ssl_client = SslClient(ssl_version=OpenSslVersionEnum.SSLV23,
                               sock=sock,
                               ssl_verify=OpenSslVerifyEnum.NONE)
        ssl_client.do_handshake()
        self.ssl_client = ssl_client
        self.cert = ssl_client.get_peer_certificate()

        self.assertEqual(HostnameValidationResultEnum.NAME_MATCHES_SAN,
                         self.cert.matches_hostname('www.google.fr'))
        self.assertEqual(HostnameValidationResultEnum.NAME_DOES_NOT_MATCH,
                         self.cert.matches_hostname('www.tests.com'))
예제 #25
0
    def __init__(self,
                 client_certificate_chain_path,
                 client_key_path,
                 client_key_type=SSL_FILETYPE_PEM,
                 client_key_password=''):
        # type: (str, str, int, Optional[str]) -> None
        """Create a container for SSL/TLS client authentication settings.

        Args:
            client_certificate_chain_path (str): Path to the client's certificate chain.
            client_key_path (str): Path to the client's private key.
            client_key_type (int): SSL_FILETYPE_PEM or SSL_FILETYPE_DER.
            client_key_password (Optional[str]): Password needed to decrypt the private key.
        """
        self.client_certificate_chain_path = client_certificate_chain_path
        if not os.path.isfile(self.client_certificate_chain_path):
            raise ValueError('Could not open the client certificate file')

        self.client_key_path = client_key_path
        if not os.path.isfile(self.client_key_path):
            raise ValueError('Could not open the client private key file')

        self.client_key_password = client_key_password

        self.client_key_type = client_key_type
        if self.client_key_type not in [SSL_FILETYPE_ASN1, SSL_FILETYPE_PEM]:
            raise ValueError('Invalid certificate format specified')

        # Try to load the cert and key in OpenSSL; will raise an exception if something is wrong
        SslClient(client_certchain_file=self.client_certificate_chain_path,
                  client_key_file=self.client_key_path,
                  client_key_type=self.client_key_type,
                  client_key_password=self.client_key_password)
예제 #26
0
    def __init__(
        self,
        client_certificate_chain_path: str,
        client_key_path: str,
        client_key_type: OpenSslFileTypeEnum = OpenSslFileTypeEnum.PEM,
        client_key_password: str = "",
    ) -> None:
        """
        Args:
            client_certificate_chain_path: Path to the file containing the client's certificate.
            client_key_path: Path to the file containing the client's private key.
            client_key_type: The format of the key file.
            client_key_password: The password to decrypt the private key.
        """
        self.client_certificate_chain_path = client_certificate_chain_path
        if not os.path.isfile(self.client_certificate_chain_path):
            raise ValueError("Could not open the client certificate file")

        self.client_key_path = client_key_path
        if not os.path.isfile(self.client_key_path):
            raise ValueError("Could not open the client private key file")

        self.client_key_password = client_key_password

        self.client_key_type = client_key_type
        if self.client_key_type not in OpenSslFileTypeEnum:
            raise ValueError("Invalid certificate format specified")

        # Try to load the cert and key in OpenSSL; will raise an exception if something is wrong
        SslClient(
            client_certchain_file=self.client_certificate_chain_path,
            client_key_file=self.client_key_path,
            client_key_type=self.client_key_type,
            client_key_password=self.client_key_password,
        )
예제 #27
0
    def test_client_authentication_tls_1_3(self):
        # Given a server that requires client authentication
        with ModernOpenSslServer(
                client_auth_config=ClientAuthConfigEnum.REQUIRED) as server:
            # And the client provides an invalid client certificate (actually the server cert)
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((server.hostname, server.port))

            ssl_client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_3,
                                   underlying_socket=sock,
                                   ssl_verify=OpenSslVerifyEnum.NONE)

            # When doing the handshake the right error is returned
            with pytest.raises(ClientCertificateRequested):
                ssl_client.do_handshake()
예제 #28
0
    def __init__(self,
                 client_certificate_chain_path,
                 client_key_path,
                 client_key_type=OpenSslFileTypeEnum.PEM,
                 client_key_password=''):
        # type: (Text, Text, OpenSslFileTypeEnum, Optional[Text]) -> None
        """
        Args:
            client_certificate_chain_path (Text): Path to the file containing the client's certificate.
            client_key_path (Text): Path to the file containing the client's private key.
            client_key_type (OpenSslFileTypeEnum): The format of the key file.
            client_key_password (Optional[Text]): The password to decrypt the private key.
        """
        self.client_certificate_chain_path = client_certificate_chain_path
        if not os.path.isfile(self.client_certificate_chain_path):
            raise ValueError('Could not open the client certificate file')

        self.client_key_path = client_key_path
        if not os.path.isfile(self.client_key_path):
            raise ValueError('Could not open the client private key file')

        self.client_key_password = client_key_password

        self.client_key_type = client_key_type
        if self.client_key_type not in OpenSslFileTypeEnum:
            raise ValueError('Invalid certificate format specified')

        # Try to load the cert and key in OpenSSL; will raise an exception if something is wrong
        SslClient(client_certchain_file=self.client_certificate_chain_path,
                  client_key_file=self.client_key_path,
                  client_key_type=self.client_key_type,
                  client_key_password=self.client_key_password)
예제 #29
0
def get_certificate_chain(host: str, port: int) -> List[str]:

    """ Connect to the host on the port and obtain certificate chain.
     TODO: Tests against WantReadError and WantX509LookupError needed. """

    cert_chain = []

    soc = socket(AF_INET, SOCK_STREAM, proto=0)
    soc.settimeout(3)

    try:
        soc.connect((host, port))

    except gaierror:
        raise Exception(f"{host}:{port} is invalid or not known.") from None

    except timeout:
        raise Exception(f"Connection to {host}:{port} timed out.") from None

    except OverflowError:
        raise Exception(f"Illegal port: {port}. Port must be between 0-65535.") from None

    except TypeError:
        raise Exception(f"Illegal port: {port}. Port must be between 0-65535.") from None

    ssl_client = SslClient(
        ssl_version=OpenSslVersionEnum.SSLV23,
        underlying_socket=soc,
        ssl_verify=OpenSslVerifyEnum.NONE
    )

    # Add Server Name Indication (SNI) extension to the CLIENT HELLO
    ssl_client.set_tlsext_host_name(host)

    try:
        ssl_client.do_handshake()
        cert_chain = ssl_client.get_received_chain()

    except WantReadError as err:
        raise ValueError(err.strerror) from None

    except WantX509LookupError as err:
        raise ValueError(err.strerror) from None

    except OpenSSLError as err:
        raise ValueError(err) from None

    finally:
        ssl_client.shutdown()
        soc = None

    return cert_chain
예제 #30
0
    def process_task(self, server_connectivity_info, plugin_command, options_dict=None):
        ssl_version = self.SSL_VERSIONS_MAPPING[plugin_command]

        # Get the list of available cipher suites for the given ssl version
        ssl_client = SslClient(ssl_version=ssl_version)
        ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL')
        cipher_list = ssl_client.get_cipher_list()

        # Scan for every available cipher suite
        thread_pool = ThreadPool()
        for cipher in cipher_list:
            thread_pool.add_job((self._test_cipher_suite, (server_connectivity_info, ssl_version, cipher)))

        # Start processing the jobs; One thread per cipher
        thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS))

        accepted_cipher_list = []
        rejected_cipher_list = []
        errored_cipher_list = []

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptedCipherSuite):
                accepted_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, RejectedCipherSuite):
                rejected_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, ErroredCipherSuite):
                errored_cipher_list.append(cipher_result)
            else:
                raise ValueError('Unexpected result')

        # Store thread pool errors; only something completely unexpected would trigger an error
        for failed_job in thread_pool.get_error():
            (_, exception) = failed_job
            raise exception

        thread_pool.join()

        # Test for the cipher suite preference
        preferred_cipher = self._get_preferred_cipher_suite(server_connectivity_info, ssl_version, accepted_cipher_list)

        # Generate the results
        plugin_result = OpenSSLCipherSuitesResult(server_connectivity_info, plugin_command, options_dict,
                                                  preferred_cipher, accepted_cipher_list, rejected_cipher_list,
                                                  errored_cipher_list)
        return plugin_result
예제 #31
0
    def test_sct_parsing(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(('sslanalyzer.comodoca.com', 443))

        ssl_client = SslClient(underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE)
        ssl_client.set_tlsext_status_ocsp()
        ssl_client.do_handshake()
        ocsp_response = ssl_client.get_tlsext_status_ocsp_resp()
        ssl_client.shutdown()
        sock.close()

        self.assertIsNotNone(ocsp_response.as_dict()['responses'][0]['singleExtensions']['ctCertificateScts'])
예제 #32
0
    def test_client_authentication_tls_1_3(self):
        # Given a server that requires client authentication
        with ModernOpenSslServer(client_auth_config=ClientAuthConfigEnum.REQUIRED) as server:
            # And the client provides an invalid client certificate (actually the server cert)
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((server.hostname, server.port))

            ssl_client = SslClient(
                ssl_version=OpenSslVersionEnum.TLSV1_3,
                underlying_socket=sock,
                ssl_verify=OpenSslVerifyEnum.NONE,
            )

            # When doing the handshake the right error is returned
            with pytest.raises(ClientCertificateRequested):
                ssl_client.do_handshake()
예제 #33
0
 def __post_init__(self) -> None:
     # Try to load the cert and key in OpenSSL; will raise an exception if something is wrong
     SslClient(
         client_certificate_chain=self.certificate_chain_path,
         client_key=self.key_path,
         client_key_type=self.key_type,
         client_key_password=self.key_password,
     )
예제 #34
0
 def setUp(self):
     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     sock.settimeout(10)
     sock.connect(('tls13.crypto.mozilla.org', 443))
     ssl_client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_3,
                            underlying_socket=sock,
                            ssl_verify=OpenSslVerifyEnum.NONE)
     self.ssl_client = ssl_client
예제 #35
0
    def test_tls_1_3(self):
        # Given a server that supports TLS 1.3
        with ModernOpenSslServer() as server:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((server.hostname, server.port))

            ssl_client = SslClient(
                ssl_version=OpenSslVersionEnum.TLSV1_3,
                underlying_socket=sock,
                ssl_verify=OpenSslVerifyEnum.NONE
            )
            # When doing the TLS 1.3 handshake, it succeeds
            try:
                ssl_client.do_handshake()
            finally:
                ssl_client.shutdown()
예제 #36
0
    def test_get_dh_info_ecdh_x25519(self):
        with ModernOpenSslServer(cipher="ECDHE-RSA-AES256-SHA",
                                 groups="X25519") as server:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((server.hostname, server.port))

            ssl_client = SslClient(
                ssl_version=OpenSslVersionEnum.TLSV1_2,
                underlying_socket=sock,
                ssl_verify=OpenSslVerifyEnum.NONE,
            )

            try:
                ssl_client.do_handshake()
            finally:
                ssl_client.shutdown()

            dh_info = ssl_client.get_ephemeral_key()

            assert isinstance(dh_info, EcDhEphemeralKeyInfo)
            assert dh_info.type == OpenSslEvpPkeyEnum.X25519
            assert dh_info.size == 253
            assert dh_info.curve == OpenSslEcNidEnum.X25519
            assert len(dh_info.public_bytes) == 32
예제 #37
0
    def test_get_dh_info_ecdh_p256(self):
        with ModernOpenSslServer(cipher="ECDHE-RSA-AES256-SHA",
                                 groups="P-256") as server:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((server.hostname, server.port))

            ssl_client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_2,
                                   underlying_socket=sock,
                                   ssl_verify=OpenSslVerifyEnum.NONE)

            try:
                ssl_client.do_handshake()
            finally:
                ssl_client.shutdown()

            dh_info = ssl_client.get_dh_info()

            assert isinstance(dh_info, NistEcDhKeyExchangeInfo)
            assert dh_info.key_type == OpenSslEvpPkeyEnum.EC
            assert dh_info.key_size == 256
            assert dh_info.curve == OpenSslEcNidEnum.PRIME256V1
            assert len(dh_info.public_key) == 65
            assert len(dh_info.x) == 32
            assert len(dh_info.y) == 32
예제 #38
0
    def _create_tls_1_3_session(server_host: str, server_port: int) -> _nassl.SSL_SESSION:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect((server_host, server_port))

        ssl_client = SslClient(
            ssl_version=OpenSslVersionEnum.TLSV1_3,
            underlying_socket=sock,
            ssl_verify=OpenSslVerifyEnum.NONE
        )

        try:
            ssl_client.do_handshake()
            ssl_client.write(ModernOpenSslServer.HELLO_MSG)
            ssl_client.read(2048)
            session = ssl_client.get_session()

        finally:
            ssl_client.shutdown()
        return session
예제 #39
0
    def test_tls_1_3_write_early_data_fail_when_used_on_non_reused_session(self):
        # Given a server that supports TLS 1.3 and early data
        with ModernOpenSslServer(max_early_data=512) as server:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(5)
            sock.connect((server.hostname, server.port))

            # That does NOT have a previous session with the server
            ssl_client = SslClient(
                ssl_version=OpenSslVersionEnum.TLSV1_3,
                underlying_socket=sock,
                ssl_verify=OpenSslVerifyEnum.NONE
            )

            # When sending early data
            # It fails
            with pytest.raises(OpenSSLError, match='you should not call'):
                ssl_client.write_early_data(b'EARLY DATA')

            ssl_client.shutdown()
예제 #40
0
    def test(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(('www.cloudflare.com', 443))

        ssl_client = SslClient(underlying_socket=sock,
                               ssl_verify=OpenSslVerifyEnum.NONE)
        ssl_client.set_tlsext_status_ocsp()
        ssl_client.do_handshake()
        ocsp_response = ssl_client.get_tlsext_status_ocsp_resp()
        ssl_client.shutdown()

        self.assertEqual(ocsp_response.status,
                         OcspResponseStatusEnum.SUCCESSFUL)

        # Test as_text()
        self.assertIsNotNone(ocsp_response.as_text())

        # Test verify with a wrong certificate
        test_file = tempfile.NamedTemporaryFile(delete=False, mode='wt')
        test_file.write("""-----BEGIN CERTIFICATE-----
MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx
DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy
ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG
9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1
OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx
IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy
MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw
ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO
8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo
w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O
zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG
CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV
HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D
NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY
gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm
dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk
Pd2eQ9+DkopOz3UGU7c=
-----END CERTIFICATE-----""")
        test_file.close()
        self.assertRaises(OcspResponseNotTrustedError, ocsp_response.verify,
                          test_file.name)

        # No SCT extension
        self.assertFalse('singleExtensions' in ocsp_response.as_dict()
                         ['responses'][0].keys())
예제 #41
0
    def setUp(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("login.live.com", 443))

        ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE)
        ssl_client.set_tlsext_status_ocsp()
        ssl_client.do_handshake()
        self.ocsp_response = ssl_client.get_tlsext_status_ocsp_resp(
        )._ocsp_response
예제 #42
0
    def setUp(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(("login.live.com", 443))

        ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE)
        ssl_client.set_tlsext_status_ocsp()
        ssl_client.do_handshake()
        self.ocsp_response = ssl_client.get_tlsext_status_ocsp_resp()._ocsp_response
예제 #43
0
    def test_tls_1_3_write_early_data_fail_when_trying_to_send_more_than_max_early_data(
            self):
        # Given a server that supports TLS 1.3
        with ModernOpenSslServer(max_early_data=1) as server:
            # That has a previous TLS 1.3 session with the server
            session = self._create_tls_1_3_session(server.hostname,
                                                   server.port)
            self.assertTrue(session)

            # And the server only accepts 1 byte of early data
            max_early = session.get_max_early_data()
            self.assertEqual(1, max_early)

            # When creating a new connection
            with socket.socket(socket.AF_INET,
                               socket.SOCK_STREAM) as sock_early_data:
                sock_early_data.settimeout(5)
                sock_early_data.connect((server.hostname, server.port))

                ssl_client_early_data = SslClient(
                    ssl_version=OpenSslVersionEnum.TLSV1_3,
                    underlying_socket=sock_early_data,
                    ssl_verify=OpenSslVerifyEnum.NONE)

                # That re-uses the previous TLS 1.3 session
                ssl_client_early_data.set_session(session)
                self.assertEqual(OpenSslEarlyDataStatusEnum.NOT_SENT,
                                 ssl_client_early_data.get_early_data_status())

                # When sending too much early data
                # It fails
                self.assertRaisesRegex(
                    OpenSSLError, 'too much early data',
                    ssl_client_early_data.write_early_data,
                    'GET / HTTP/1.1\r\nData: {}\r\n\r\n'.format('*' *
                                                                max_early))
예제 #44
0
    def test_set_ciphersuites(self):
        # Given an SslClient for TLS 1.3
        ssl_client = SslClient(
            ssl_version=OpenSslVersionEnum.TLSV1_3,
            ssl_verify=OpenSslVerifyEnum.NONE,
            ignore_client_authentication_requests=True,
        )

        # With the default list of cipher disabled
        ssl_client.set_cipher_list("")

        # When setting a specific TLS 1.3 cipher suite as the list of supported ciphers
        ssl_client.set_ciphersuites("TLS_CHACHA20_POLY1305_SHA256")

        # That one cipher suite is the only one enabled
        ciphers = ssl_client.get_cipher_list()
        assert ["TLS_CHACHA20_POLY1305_SHA256"] == ciphers
예제 #45
0
    def test_set_ciphersuites(self):
        # Given an SslClient for TLS 1.3
        ssl_client = SslClient(
            ssl_version=OpenSslVersionEnum.TLSV1_3,
            ssl_verify=OpenSslVerifyEnum.NONE,
            ignore_client_authentication_requests=True,
        )

        # With the default list of cipher disabled
        ssl_client.set_cipher_list('')

        # When setting a specific TLS 1.3 cipher suite as the list of supported ciphers
        ssl_client.set_ciphersuites('TLS_CHACHA20_POLY1305_SHA256')

        # That one cipher suite is the only one enabled
        ciphers = ssl_client.get_cipher_list()
        assert ['TLS_CHACHA20_POLY1305_SHA256'] == ciphers
예제 #46
0
    def test_get_verified_chain_but_validation_failed(self):
        # Given an SslClient connecting to Google
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(('www.google.com', 443))

        ssl_client = SslClient(
            ssl_version=OpenSslVersionEnum.TLSV1_2,
            underlying_socket=sock,

            # That is configured to silently fail validation
            ssl_verify=OpenSslVerifyEnum.NONE
        )

        # When doing a TLS handshake, it succeeds
        try:
            ssl_client.do_handshake()

            # And when requesting the verified certificate chain
            with pytest.raises(CouldNotBuildVerifiedChain):
                # It fails because certificate validation failed
                ssl_client.get_verified_chain()
        finally:
            ssl_client.shutdown()
예제 #47
0
    def test_get_verified_chain(self):
        # Given an SslClient connecting to Google
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect(('www.yahoo.com', 443))
        print(str(Path(__file__).absolute().parent / 'google_roots.pem'))
        ssl_client = SslClient(
            ssl_version=OpenSslVersionEnum.TLSV1_2,
            underlying_socket=sock,

            # That is configured to properly validate certificates
            ssl_verify=OpenSslVerifyEnum.PEER,
            ssl_verify_locations=str(Path(__file__).absolute().parent / 'mozilla.pem')
        )

        # When doing a TLS handshake, it succeeds
        try:
            ssl_client.do_handshake()

            # And when requesting the verified certificate chain, it returns it
            assert ssl_client.get_verified_chain()
        finally:
            ssl_client.shutdown()
예제 #48
0
from nassl.ssl_client import OpenSslVersionEnum, SslClient, OpenSslVerifyEnum
import socket

mozilla_store = 'tests/mozilla.pem'
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
sock.connect(('www.yahoo.com', 443))

ssl_client = SslClient(
    ssl_version=OpenSslVersionEnum.TLSV1_2,
    underlying_socket=sock,
    ssl_verify=OpenSslVerifyEnum.PEER,
    ssl_verify_locations=mozilla_store,
)
ssl_client.set_tlsext_status_ocsp()
ssl_client.do_handshake()

print('Received certificate chain')
for pem_cert in ssl_client.get_received_chain():
    print(pem_cert)

print('Verified certificate chain')
for pem_cert in ssl_client.get_verified_chain():
    print(pem_cert)

print('OCSP Stapling')
ocsp_resp = ssl_client.get_tlsext_status_ocsp_resp()
if ocsp_resp:
    ocsp_resp.verify(mozilla_store)
    print(ocsp_resp.as_dict())
예제 #49
0
    def test_tls_1_3_write_early_data_does_not_finish_handshake(self):
        # Given a server that supports TLS 1.3 and early data
        with ModernOpenSslServer(max_early_data=512) as server:
            # That has a previous TLS 1.3 session with the server
            session = self._create_tls_1_3_session(server.hostname, server.port)
            assert session

            # And the server accepts early data
            max_early = session.get_max_early_data()
            assert max_early > 0

            # When creating a new connection
            sock_early_data = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock_early_data.settimeout(5)
            sock_early_data.connect((server.hostname, server.port))

            ssl_client_early_data = SslClient(
                ssl_version=OpenSslVersionEnum.TLSV1_3,
                underlying_socket=sock_early_data,
                ssl_verify=OpenSslVerifyEnum.NONE
            )

            # That re-uses the previous TLS 1.3 session
            ssl_client_early_data.set_session(session)
            assert OpenSslEarlyDataStatusEnum.NOT_SENT == ssl_client_early_data.get_early_data_status()

            # When sending early data
            ssl_client_early_data.write_early_data(b'EARLY DATA')

            # It succeeds
            assert not ssl_client_early_data.is_handshake_completed()
            assert OpenSslEarlyDataStatusEnum.REJECTED == ssl_client_early_data.get_early_data_status()

            # And after completing the handshake, the early data was accepted
            ssl_client_early_data.do_handshake()
            assert OpenSslEarlyDataStatusEnum.ACCEPTED == ssl_client_early_data.get_early_data_status()

            ssl_client_early_data.shutdown()
 def get_ssl3_cipher_list(self):
     """Returns list of cipher suites available for protocol SSL 3.0
     """
     ssl_client = SslClient(ssl_version=SSLV3)
     ssl_client.set_cipher_list('SSLv3')
     return ssl_client.get_cipher_list()