def invalid_certificate_scans_parameters(*args, **kwargs): certificate = kwargs["certificate"] certificate_scan = kwargs["certificate_scan"] protocol = kwargs["protocol"] if certificate_scan == CertificateScan.CIPHER_SUITE_SCAN: if "openssl-1.0.2" in get_flag(S2N_PROVIDER_VERSION): # SSLyze scan results in rejected ciphers that should have been accepted # for TLS 1.2 if protocol == Protocols.TLS12: return True if "fips" in get_flag(S2N_PROVIDER_VERSION): # BUG_IN_SSLYZE / TLS version supported assertion failures for ECDSA scans # in SSLv3 and RSA with TLS version < 1.2 with fips libcryptos if "ECDSA" in certificate.name and protocol == Protocols.SSLv3: return True if "RSA" in certificate.name and protocol in [ Protocols.SSLv3, Protocols.TLS10, Protocols.TLS11 ]: return True elif certificate_scan == CertificateScan.ELLIPTIC_CURVE_SCAN: # SSLyze curves scan errors when given ECDSA certs if "ECDSA" in certificate.name: return True # SSLyze curves scan fails to validate with openssl 1.0.2 fips if "openssl-1.0.2-fips" in get_flag(S2N_PROVIDER_VERSION): return True return invalid_test_parameters(*args, **kwargs)
def test_well_known_endpoints(managed_process, protocol, endpoint, provider, cipher): port = "443" client_options = ProviderOptions(mode=Provider.ClientMode, host=endpoint, port=port, insecure=False, trust_store=TRUST_STORE_BUNDLE, protocol=protocol, cipher=cipher) if get_flag(S2N_FIPS_MODE) is True: client_options.trust_store = "../integration/trust-store/ca-bundle.trust.crt" else: client_options.trust_store = "../integration/trust-store/ca-bundle.crt" # expect_stderr=True because S2N sometimes receives OCSP responses: # https://github.com/aws/s2n-tls/blob/14ed186a13c1ffae7fbb036ed5d2849ce7c17403/bin/echo.c#L180-L184 client = managed_process(provider, client_options, timeout=5, expect_stderr=True) expected_result = EXPECTED_RESULTS.get((endpoint, cipher), None) for results in client.get_results(): results.assert_success() if expected_result is not None: assert to_bytes(expected_result['cipher']) in results.stdout assert to_bytes(expected_result['kem']) in results.stdout
def test_well_known_endpoints(managed_process, protocol, endpoint, provider, cipher): port = "443" client_options = ProviderOptions(mode=Provider.ClientMode, host=endpoint, port=port, insecure=False, trust_store=TRUST_STORE_BUNDLE, protocol=protocol, cipher=cipher) if get_flag(S2N_FIPS_MODE) is True: client_options.trust_store = "../integration/trust-store/ca-bundle.trust.crt" else: client_options.trust_store = "../integration/trust-store/ca-bundle.crt" client = managed_process(provider, client_options, timeout=5) expected_result = EXPECTED_RESULTS.get((endpoint, cipher), None) for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 if expected_result is not None: assert bytes( expected_result['cipher'].encode('utf-8')) in results.stdout assert bytes( expected_result['kem'].encode('utf-8')) in results.stdout
def setup_client(self): """ Using the passed ProviderOptions, create a command line. """ cmd_line = [] if self.options.use_mainline_version is True: cmd_line.append('s2nc_head') else: cmd_line.append('s2nc') cmd_line.append('--non-blocking') # Tests requiring reconnects can't wait on echo data, # but all other tests can. if self.options.reconnect is not True: cmd_line.append('-e') if self.options.use_session_ticket is False: cmd_line.append('-T') if self.options.insecure is True: cmd_line.append('--insecure') elif self.options.trust_store: cmd_line.extend(['-f', self.options.trust_store]) elif self.options.cert: cmd_line.extend(['-f', self.options.cert]) if self.options.reconnect is True: cmd_line.append('-r') # If the test provided a cipher (security policy) that is compatible with # s2n, we'll use it. Otherwise, default to the appropriate `test_all` policy. cipher_prefs = 'test_all_tls12' if self.options.protocol is Protocols.TLS13: cipher_prefs = 'test_all' if self.options.cipher and self.options.cipher.s2n: cipher_prefs = self.options.cipher.name cmd_line.extend(['-c', cipher_prefs]) if self.options.use_client_auth: if self.options.key: cmd_line.extend(['--key', self.options.key]) if self.options.cert: cmd_line.extend(['--cert', self.options.cert]) if get_flag(S2N_FIPS_MODE): cmd_line.append("--enter-fips-mode") if self.options.enable_client_ocsp: cmd_line.extend(["--status"]) if self.options.extra_flags is not None: cmd_line.extend(self.options.extra_flags) cmd_line.extend([self.options.host, self.options.port]) # Clients are always ready to connect self.set_provider_ready() return cmd_line
def test_well_known_endpoints(managed_process, protocol, endpoint): port = "443" client_options = ProviderOptions(mode=Provider.ClientMode, host=endpoint['endpoint'], port=port, insecure=False, client_trust_store=TRUST_STORE_BUNDLE, protocol=protocol) if get_flag(S2N_FIPS_MODE) is True: client_options.client_trust_store = "../integration/trust-store/ca-bundle.trust.crt" else: client_options.client_trust_store = "../integration/trust-store/ca-bundle.crt" if 'cipher_preference_version' in endpoint: client_options.cipher = endpoint['cipher_preference_version'] client = managed_process(S2N, client_options, timeout=5) for results in client.get_results(): if results.exception is not None or results.exit_code != 0: assert endpoint['endpoint'] in expected_failures if 'expected_cipher' in endpoint: assert bytes( endpoint['expected_cipher'].encode('utf-8')) in results.stdout
def setup_server(self): # s2nd prints this message after it begins listening for connections self.ready_to_test_marker = 'Listening on' """ Using the passed ProviderOptions, create a command line. """ cmd_line = [] if self.options.use_mainline_version is True: cmd_line.append('s2nd_head') else: cmd_line.append('s2nd') cmd_line.extend(['-X', '--self-service-blinding', '--non-blocking']) if self.options.key is not None: cmd_line.extend(['--key', self.options.key]) if self.options.cert is not None: cmd_line.extend(['--cert', self.options.cert]) if self.options.insecure is True: cmd_line.append('--insecure') elif self.options.trust_store: cmd_line.extend(['-t', self.options.trust_store]) elif self.options.cert: cmd_line.extend(['-t', self.options.cert]) # If the test provided a cipher (security policy) that is compatible with # s2n, we'll use it. Otherwise, default to the appropriate `test_all` policy. cipher_prefs = 'test_all_tls12' if self.options.protocol is Protocols.TLS13: cipher_prefs = 'test_all' if self.options.cipher and self.options.cipher.s2n: cipher_prefs = self.options.cipher.name cmd_line.extend(['-c', cipher_prefs]) if self.options.use_client_auth is True: cmd_line.append('-m') if self.options.use_session_ticket is False: cmd_line.append('-T') if self.options.reconnects_before_exit is not None: cmd_line.append( '--max-conns={}'.format(self.options.reconnects_before_exit)) if get_flag(S2N_FIPS_MODE): cmd_line.append("--enter-fips-mode") if self.options.ocsp_response is not None: cmd_line.extend(["--ocsp", self.options.ocsp_response]) if self.options.extra_flags is not None: cmd_line.extend(self.options.extra_flags) cmd_line.extend([self.options.host, self.options.port]) return cmd_line
def supports_protocol(cls, protocol, with_cert=None): # Disable TLS 1.3 tests for all libcryptos that don't support 1.3 if all([ libcrypto not in get_flag(S2N_PROVIDER_VERSION) for libcrypto in TLS_13_LIBCRYPTOS ]) and protocol == Protocols.TLS13: return False return True
def invalid_sslyze_scan_parameters(*args, **kwargs): scan_command = kwargs["scan_command"] protocol = kwargs["protocol"] # BUG_IN_SSLYZE error in TLS compression and session renegotiation scans # in fips libcryptos when TLS version < 1.3 if "fips" in get_flag( S2N_PROVIDER_VERSION) and protocol != Protocols.TLS13: if scan_command in [ sslyze.ScanCommand.TLS_COMPRESSION, sslyze.ScanCommand.SESSION_RENEGOTIATION ]: return True # BUG_IN_SSLYZE error for session resumption scan with openssl 1.0.2 fips if "openssl-1.0.2-fips" in get_flag(S2N_PROVIDER_VERSION): if scan_command == sslyze.ScanCommand.SESSION_RESUMPTION: return True return invalid_test_parameters(*args, **kwargs)
def supports_cipher(cls, cipher, with_curve=None): # Disable chacha20 tests in unsupported libcryptos if any([ libcrypto in get_flag(S2N_PROVIDER_VERSION) for libcrypto in [ "openssl-1.0.2", "libressl" ] ]) and "CHACHA20" in cipher.name: return False return True
def test_s2n_client_ocsp_response(managed_process, cipher, provider, other_provider, curve, protocol, certificate): if "boringssl" in get_flag(S2N_PROVIDER_VERSION): pytest.skip("s2n-tls client with boringssl does not support ocsp") port = next(available_ports) random_bytes = data_bytes(128) client_options = ProviderOptions(mode=Provider.ClientMode, port=port, cipher=cipher, curve=curve, protocol=protocol, insecure=True, data_to_send=random_bytes, enable_client_ocsp=True) server_options = ProviderOptions( mode=Provider.ServerMode, port=port, cipher=cipher, curve=curve, protocol=protocol, key=certificate.key, cert=certificate.cert, ocsp_response={ "RSA": TEST_OCSP_DIRECTORY + "ocsp_response.der", "EC": TEST_OCSP_DIRECTORY + "ocsp_ecdsa_response.der" }.get(certificate.algorithm), ) kill_marker = None if provider == GnuTLS: kill_marker = random_bytes server = managed_process(provider, server_options, timeout=30, kill_marker=kill_marker) client = managed_process(S2N, client_options, timeout=30) for client_results in client.get_results(): client_results.assert_success() assert b"OCSP response received" in client_results.stdout for server_results in server.get_results(): server_results.assert_success() # Avoid debugging information that sometimes gets inserted after the first character. assert random_bytes[1:] in server_results.stdout or random_bytes[ 1:] in server_results.stderr
def supports_signature(cls, signature): # Disable RSA_PSS_RSAE_SHA256 in unsupported libcryptos if any([ libcrypto in get_flag(S2N_PROVIDER_VERSION) for libcrypto in [ "openssl-1.0.2", "libressl", "boringssl" ] ]) and signature == Signatures.RSA_PSS_RSAE_SHA256: return False return True
def test_s2nc_to_s2nd_pq_handshake(managed_process, protocol, certificate, client_cipher, server_cipher, provider, other_provider): # Incorrect cipher is negotiated when both ciphers are PQ_TLS_1_0_2020_12 with # openssl 1.0.2, boringssl, and libressl libcryptos if all([ client_cipher == Ciphers.PQ_TLS_1_0_2020_12, server_cipher == Ciphers.PQ_TLS_1_0_2020_12, any([ libcrypto in get_flag(S2N_PROVIDER_VERSION) for libcrypto in ["boringssl", "libressl", "openssl-1.0.2"] ]) ]): pytest.skip() port = next(available_ports) client_options = ProviderOptions(mode=Provider.ClientMode, port=port, insecure=True, cipher=client_cipher, protocol=protocol) server_options = ProviderOptions(mode=Provider.ServerMode, port=port, protocol=protocol, cipher=server_cipher, cert=certificate.cert, key=certificate.key) server = managed_process(S2N, server_options, timeout=5) client = managed_process(S2N, client_options, timeout=5) if pq_enabled(): expected_result = EXPECTED_RESULTS.get((client_cipher, server_cipher), None) else: # If PQ is not enabled in s2n, we expect classic handshakes to be negotiated. # Leave the expected cipher blank, as there are multiple possibilities - the # important thing is that kem and kem_group are NONE. expected_result = {"cipher": "", "kem": "NONE", "kem_group": "NONE"} # Client and server are both s2n; can make meaningful assertions about negotiation for both for results in client.get_results(): results.assert_success() assert_s2n_negotiation_parameters(results, expected_result) for results in server.get_results(): results.assert_success() assert_s2n_negotiation_parameters(results, expected_result)
def supports_cipher(cls, cipher, with_curve=None): if "openssl-1.0.2" in get_flag(S2N_PROVIDER_VERSION) and with_curve is not None: invalid_ciphers = [ Ciphers.ECDHE_RSA_AES128_SHA, Ciphers.ECDHE_RSA_AES256_SHA, Ciphers.ECDHE_RSA_AES128_SHA256, Ciphers.ECDHE_RSA_AES256_SHA384, Ciphers.ECDHE_RSA_AES128_GCM_SHA256, Ciphers.ECDHE_RSA_AES256_GCM_SHA384, ] # OpenSSL 1.0.2 and 1.0.2-FIPS can't find a shared cipher with S2N # when P-384 is used, but I can't find any reason why. if with_curve is Curves.P384 and cipher in invalid_ciphers: return False return True
class OpenSSL(Provider): _version = get_flag(S2N_PROVIDER_VERSION) def __init__(self, options: ProviderOptions): self.ready_to_send_input_marker = None Provider.__init__(self, options) def _join_ciphers(self, ciphers): """ Given a list of ciphers, join the names with a ':' like OpenSSL expects """ assert type(ciphers) is list cipher_list = [] for c in ciphers: cipher_list.append(c.name) ciphers = ':'.join(cipher_list) return ciphers def _cipher_to_cmdline(self, cipher): cmdline = list() ciphers = [] if type(cipher) is list: # In the case of a cipher list we need to be sure TLS13 specific ciphers aren't # mixed with ciphers from previous versions is_tls13_or_above = (cipher[0].min_version >= Protocols.TLS13) mismatch = [ c for c in cipher if (c.min_version >= Protocols.TLS13) != is_tls13_or_above ] if len(mismatch) > 0: raise Exception( "Cannot combine ciphers for TLS1.3 or above with older ciphers: {}" .format([c.name for c in cipher])) ciphers.append(self._join_ciphers(cipher)) else: is_tls13_or_above = (cipher.min_version >= Protocols.TLS13) ciphers.append(cipher.name) if is_tls13_or_above: cmdline.append('-ciphersuites') else: cmdline.append('-cipher') return cmdline + ciphers @classmethod def get_version(cls): return cls._version @classmethod def supports_protocol(cls, protocol, with_cert=None): if protocol is Protocols.TLS13: if 'openssl-1.1.1' in OpenSSL.get_version(): return True else: return False return True @classmethod def supports_cipher(cls, cipher, with_curve=None): is_openssl_111 = "openssl-1.1.1" in OpenSSL.get_version() if is_openssl_111 and cipher.openssl1_1_1 is False: return False if not is_openssl_111: # OpenSSL 1.0.2 does not have ChaChaPoly if 'CHACHA20' in cipher.name: return False if cipher.fips is False and "fips" in OpenSSL.get_version(): return False if "openssl-1.0.2" in OpenSSL.get_version() and with_curve is not None: invalid_ciphers = [ Ciphers.ECDHE_RSA_AES128_SHA, Ciphers.ECDHE_RSA_AES256_SHA, Ciphers.ECDHE_RSA_AES128_SHA256, Ciphers.ECDHE_RSA_AES256_SHA384, Ciphers.ECDHE_RSA_AES128_GCM_SHA256, Ciphers.ECDHE_RSA_AES256_GCM_SHA384, ] # OpenSSL 1.0.2 and 1.0.2-FIPS can't find a shared cipher with S2N # when P-384 is used, but I can't find any reason why. if with_curve is Curves.P384 and cipher in invalid_ciphers: return False return True def setup_client(self): # s_client prints this message before it is ready to send/receive data self.ready_to_send_input_marker = 'Verify return code' cmd_line = ['openssl', 's_client'] cmd_line.extend( ['-connect', '{}:{}'.format(self.options.host, self.options.port)]) # Additional debugging that will be captured incase of failure cmd_line.extend(['-debug', '-tlsextdebug', '-state']) if self.options.key is not None: cmd_line.extend(['-key', self.options.key]) # Unlike s2n, OpenSSL allows us to be much more specific about which TLS # protocol to use. if self.options.protocol == Protocols.TLS13: cmd_line.append('-tls1_3') elif self.options.protocol == Protocols.TLS12: cmd_line.append('-tls1_2') elif self.options.protocol == Protocols.TLS11: cmd_line.append('-tls1_1') elif self.options.protocol == Protocols.TLS10: cmd_line.append('-tls1') if self.options.cipher is not None: cmd_line.extend(self._cipher_to_cmdline(self.options.cipher)) if self.options.curve is not None: cmd_line.extend(['-curves', str(self.options.curve)]) if self.options.use_client_auth: if self.options.key: cmd_line.extend(['-key', self.options.key]) if self.options.cert: cmd_line.extend(['-cert', self.options.cert]) if self.options.reconnect is True: cmd_line.append('-reconnect') if self.options.extra_flags is not None: cmd_line.extend(self.options.extra_flags) if self.options.server_name is not None: cmd_line.extend(['-servername', self.options.server_name]) if self.options.verify_hostname is not None: cmd_line.extend(['-verify_hostname', self.options.server_name]) # Clients are always ready to connect self.set_provider_ready() return cmd_line def setup_server(self): # s_server prints this message before it is ready to send/receive data self.ready_to_test_marker = 'ACCEPT' cmd_line = ['openssl', 's_server'] cmd_line.extend(['-accept', '{}'.format(self.options.port)]) if self.options.reconnects_before_exit is not None: # If the user request a specific reconnection count, set it here cmd_line.extend( ['-naccept', str(self.options.reconnects_before_exit)]) else: # Exit after the first connection by default cmd_line.extend(['-naccept', '1']) # Additional debugging that will be captured incase of failure cmd_line.extend(['-debug', '-tlsextdebug', '-state']) if self.options.cert is not None: cmd_line.extend(['-cert', self.options.cert]) if self.options.key is not None: cmd_line.extend(['-key', self.options.key]) # Unlike s2n, OpenSSL allows us to be much more specific about which TLS # protocol to use. if self.options.protocol == Protocols.TLS13: cmd_line.append('-tls1_3') elif self.options.protocol == Protocols.TLS12: cmd_line.append('-tls1_2') elif self.options.protocol == Protocols.TLS11: cmd_line.append('-tls1_1') elif self.options.protocol == Protocols.TLS10: cmd_line.append('-tls1') if self.options.cipher is not None: cmd_line.extend(self._cipher_to_cmdline(self.options.cipher)) if self.options.cipher.parameters is not None: cmd_line.extend(['-dhparam', self.options.cipher.parameters]) if self.options.curve is not None: cmd_line.extend(['-curves', str(self.options.curve)]) if self.options.use_client_auth is True: # We use "Verify" instead of "verify" to require a client cert cmd_line.extend(['-Verify', '1']) if self.options.extra_flags is not None: cmd_line.extend(self.options.extra_flags) return cmd_line
}, { "endpoint": "s3.amazonaws.com" }, { "endpoint": "twitter.com" }, { "endpoint": "wikipedia.org" }, { "endpoint": "yahoo.com" }, ] if get_flag(S2N_NO_PQ, False) is False: # If PQ was compiled into S2N, test the PQ preferences against KMS pq_endpoints = [{ "endpoint": "kms.us-east-1.amazonaws.com", "cipher_preference_version": Ciphers.KMS_PQ_TLS_1_0_2019_06, "expected_cipher": "ECDHE-BIKE-RSA-AES256-GCM-SHA384" }, { "endpoint": "kms.us-east-1.amazonaws.com", "cipher_preference_version": Ciphers.PQ_SIKE_TEST_TLS_1_0_2019_11, "expected_cipher": "ECDHE-SIKE-RSA-AES256-GCM-SHA384" }] ENDPOINTS.extend(pq_endpoints) # Wikipedia still fails when connecting from the codebuild images expected_failures = ['wikipedia.org']
def pq_enabled(): """ Returns true or false to indicate whether PQ crypto is enabled in s2n """ return not (get_flag(S2N_NO_PQ, False) or get_flag(S2N_FIPS_MODE, False))
def invalid_test_parameters(*args, **kwargs): """ Determine if the parameters chosen for a test makes sense. This function returns True or False, indicating whether a test should be "deselected" based on the arguments. """ protocol = kwargs.get('protocol') provider = kwargs.get('provider') certificate = kwargs.get('certificate') client_certificate = kwargs.get('client_certificate') cipher = kwargs.get('cipher') curve = kwargs.get('curve') # Only TLS1.3 supports RSA-PSS-PSS certificates # (Earlier versions support RSA-PSS signatures, just via RSA-PSS-RSAE) if protocol and protocol is not Protocols.TLS13: if client_certificate and client_certificate.algorithm == 'RSAPSS': return True if certificate and certificate.algorithm == 'RSAPSS': return True if provider is not None and not provider.supports_protocol(protocol): return True if cipher is not None: # TODO Remove this check once the pq-enabled update is complete; once complete, # s2n will ignore PQ ciphers if PQ is not enabled, so we won't have to perform # this check in the tests (See discussion in PR #2426). if cipher.pq and (get_flag(S2N_NO_PQ, False) or get_flag(S2N_FIPS_MODE, False)): return True # If the selected protocol doesn't allow the cipher, don't test if protocol is not None: if cipher.min_version > protocol: return True # Ciphersuites prior to TLS13 can not be used with TLS13 # https://wiki.openssl.org/index.php/TLS1.3#Differences_with_TLS1.2_and_below if protocol is Protocols.TLS13 and cipher.min_version < protocol: return True if provider is not None and not provider.supports_cipher( cipher, with_curve=curve): return True # If we are using a cipher that depends on a specific certificate algorithm # deselect the test if the wrong certificate is used. if certificate is not None: if protocol is not None and provider.supports_protocol( protocol, with_cert=certificate) is False: return True if cipher is not None and certificate.compatible_with_cipher( cipher) is False: return True # If the curve is specified, then all signatures must use that curve if curve: if certificate and not certificate.compatible_with_curve(curve): return True if client_certificate and not client_certificate.compatible_with_curve( curve): return True # Prevent situations like using X25519 with TLS1.2 if curve is not None: if protocol is not None and curve.min_protocol > protocol: return True return False