def test_s2n_server_happy_path(managed_process, cipher, provider, curve, protocol, certificate): port = next(available_ports) # s2nd can receive large amounts of data because all the data is # echo'd to stdout unmodified. This lets us compare received to # expected easily. # We purposefully send a non block aligned number to make sure # nothing blocks waiting for more data. random_bytes = data_bytes(65519) client_options = ProviderOptions(mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, cert=certificate, curve=curve, data_to_send=random_bytes, insecure=True, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert # Passing the type of client and server as a parameter will # allow us to use a fixture to enumerate all possibilities. server = managed_process(S2N, server_options, timeout=5) client = managed_process(provider, client_options, timeout=5) # The client will be one of all supported providers. We # just want to make sure there was no exception and that # the client exited cleanly. for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 expected_version = get_expected_s2n_version(protocol, provider) # The server is always S2N in this test, so we can examine # the stdout reliably. for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 assert bytes("Actual protocol version: {}".format( expected_version).encode('utf-8')) in results.stdout assert random_bytes in results.stdout if provider is not S2N: assert bytes("Cipher negotiated: {}".format( cipher.name).encode('utf-8')) in results.stdout
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 test_hrr_with_s2n_as_server(managed_process, cipher, provider, curve, protocol, certificate): port = next(available_ports) random_bytes = data_bytes(64) client_options = ProviderOptions( mode=Provider.ClientMode, port=port, cipher=cipher, data_to_send=random_bytes, insecure=True, curve=curve, extra_flags=['-msg', '-curves', 'X448:' + str(curve)], protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server_options.extra_flags = None # Passing the type of client and server as a parameter will # allow us to use a fixture to enumerate all possibilities. server = managed_process(S2N, server_options, timeout=5) client = managed_process(provider, client_options, timeout=5) # The client should connect and return without error for results in server.get_results(): results.assert_success() assert random_bytes in results.stdout assert to_bytes("Curve: {}".format( CURVE_NAMES[curve.name])) in results.stdout assert random_bytes in results.stdout assert S2N_HRR_MARKER in results.stdout client_hello_count = 0 server_hello_count = 0 finished_count = 0 # HRR random data Refer: https://tools.ietf.org/html/rfc8446#section-4.1.3 marker = b"cf 21 ad 74 e5 9a 61 11 be 1d" for results in client.get_results(): results.assert_success() assert marker in results.stdout client_hello_count = results.stdout.count(b'ClientHello') server_hello_count = results.stdout.count(b'ServerHello') finished_count = results.stdout.count(b'Finished') assert client_hello_count == 2 assert server_hello_count == 2 assert finished_count == 2
def test_s2n_server_key_update(managed_process, cipher): host = "localhost" port = next(available_ports) update_requested = b"K" server_data = data_bytes(10) client_data = data_bytes(10) starting_marker = "Verify return code" key_update_marker = "KEYUPDATE" send_marker_list = [starting_marker, key_update_marker] client_options = ProviderOptions( mode=Provider.ClientMode, host=host, port=port, cipher=cipher, data_to_send=[update_requested, client_data], insecure=True, protocol=Protocols.TLS13, ) server_options = copy.copy(client_options) server_options.mode = Provider.ServerMode server_options.key = "../pems/ecdsa_p384_pkcs1_key.pem" server_options.cert = "../pems/ecdsa_p384_pkcs1_cert.pem" server_options.data_to_send = [server_data] server = managed_process(S2N, server_options, send_marker=[str(client_data)], timeout=5) client = managed_process( OpenSSL, client_options, send_marker=send_marker_list, close_marker=str(server_data), timeout=5, ) for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 assert key_update_marker in str(results.stderr) assert server_data in results.stdout for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 assert client_data in results.stdout
def test_client_auth_with_s2n_client_with_cert(managed_process, cipher, curve, protocol, certificate): port = next(available_ports) random_bytes = data_bytes(64) client_options = ProviderOptions( mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, curve=curve, data_to_send=random_bytes, use_client_auth=True, client_key_file=certificate.key, client_certificate_file=certificate.cert, client_trust_store=Certificates.RSA_2048_SHA256_WILDCARD.cert, insecure=False, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = Certificates.RSA_2048_SHA256_WILDCARD.key server_options.cert = Certificates.RSA_2048_SHA256_WILDCARD.cert # Passing the type of client and server as a parameter will # allow us to use a fixture to enumerate all possibilities. server = managed_process(OpenSSL, server_options, timeout=5) client = managed_process(S2N, client_options, timeout=5) # The client should connect and return without error for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 # Openssl should indicate the procotol version in a successful connection. for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 assert random_bytes in results.stdout if protocol is Protocols.TLS13: message = bytes( "SSL_accept:SSLv3/TLS read client certificate\nSSL_accept:SSLv3/TLS read certificate verify\nSSL_accept:SSLv3/TLS read finished" .encode('utf-8')) else: message = bytes( 'SSL_accept:SSLv3/TLS read client certificate\nSSL_accept:SSLv3/TLS read client key exchange\nSSL_accept:SSLv3/TLS read certificate verify\nSSL_accept:SSLv3/TLS read change cipher spec\nSSL_accept:SSLv3/TLS read finished' .encode('utf-8')) assert message in results.stderr
def test_s2nd_tls13_negotiates_tls12(managed_process, cipher, curve, protocol, provider, certificate): port = next(available_ports) random_bytes = data_bytes(24) client_options = ProviderOptions(mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, curve=curve, data_to_send=random_bytes, insecure=True, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server_options.protocol = Protocols.TLS13 server = managed_process(S2N, server_options, timeout=5) client = managed_process(provider, client_options, timeout=5) server_version = get_expected_s2n_version(Protocols.TLS13, provider) actual_version = get_expected_s2n_version(protocol, provider) for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 if provider is S2N: # The client will get the server version from the SERVER HELLO, which will be the negotiated version assert bytes("Server protocol version: {}".format( actual_version).encode('utf-8')) in results.stdout assert bytes("Actual protocol version: {}".format( actual_version).encode('utf-8')) in results.stdout elif provider is OpenSSL: # This check cares about other providers because we want to know that they did negotiate the version # that our S2N server intended to negotiate. openssl_version = get_expected_openssl_version(protocol) assert bytes("Protocol : {}".format(openssl_version).encode( 'utf-8')) in results.stdout for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 assert bytes("Server protocol version: {}".format( server_version).encode('utf-8')) in results.stdout assert bytes("Actual protocol version: {}".format( actual_version).encode('utf-8')) in results.stdout assert random_bytes in results.stdout
def test_client_auth_with_s2n_server_using_nonmatching_certs( managed_process, cipher, provider, protocol, certificate, client_certificate): port = next(available_ports) if protocol < Protocols.TLS12 and client_certificate.algorithm == 'EC': pytest.xfail( "Client auth with ECDSA certs is current broken for versions < TLS1.2" ) client_options = ProviderOptions(mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, data_to_send=b'', use_client_auth=True, key=client_certificate.key, cert=client_certificate.cert, trust_store=certificate.cert, insecure=False, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert # Tell the server to expect the wrong certificate server_options.trust_store = Certificates.RSA_2048_SHA256_WILDCARD.cert server = managed_process(S2N, server_options, timeout=5) client = managed_process(OpenSSL, client_options, timeout=5) # Openssl should tell us that a certificate was sent, but the handshake did not complete for results in client.get_results(): assert results.exception is None assert b'write client certificate' in results.stderr assert b'write certificate verify' in results.stderr # TLS1.3 OpenSSL fails after the handshake, but pre-TLS1.3 fails during if protocol is not Protocols.TLS13: assert results.exit_code != 0 assert_openssl_handshake_complete(results, False) # S2N should tell us that mutual authentication failed due to an untrusted cert for results in server.get_results(): assert results.exception is None assert results.exit_code != 0 assert b'Certificate is untrusted' in results.stderr assert b'Error: Mutual Auth was required, but not negotiated' in results.stderr assert_s2n_handshake_complete(results, protocol, provider, False)
def test_s2n_server_with_early_data_max_exceeded(managed_process, tmp_path, cipher, curve, protocol, provider, certificate, excess_early_data): ticket_file = str(tmp_path / TICKET_FILE) early_data_file = str(tmp_path / EARLY_DATA_FILE) early_data = get_early_data_bytes( early_data_file, MAX_EARLY_DATA + excess_early_data) options = ProviderOptions( port=next(available_ports), cipher=cipher, curve=curve, protocol=protocol, insecure=True, use_session_ticket=True, data_to_send=DATA_TO_SEND, ) options.ticket_file = ticket_file options.early_data_file = early_data_file options.max_early_data = MAX_EARLY_DATA + excess_early_data get_ticket_from_s2n_server(options, managed_process, provider, certificate) options.max_early_data = MAX_EARLY_DATA client_options = copy.copy(options) client_options.mode = Provider.ClientMode server_options = copy.copy(options) server_options.mode = Provider.ServerMode s2n_server = managed_process(S2N, server_options) client = managed_process(provider, client_options) for results in client.get_results(): """ We can't make any assertions about the client exit_code. To avoid blinding delays, s2nd doesn't call s2n_shutdown for a failed negotiation. That means that instead of sending close_notify, we just close the socket. Whether the peer interprets this as a failure or EoF depends on its state. """ assert results.exception is None assert DATA_TO_SEND not in results.stdout for results in s2n_server.get_results(): assert results.exception is None assert results.exit_code != 0 # Full early data should not be reported assert early_data not in results.stdout # Partial early data should be reported assert (to_bytes(S2N_EARLY_DATA_RECV_MARKER) + early_data[:MAX_EARLY_DATA]) in results.stdout assert to_bytes("Bad message encountered") in results.stderr
def test_s2nd_tls13_negotiates_tls12(managed_process, cipher, curve, protocol, provider, certificate): port = next(available_ports) random_bytes = data_bytes(24) client_options = ProviderOptions(mode=Provider.ClientMode, port=port, cipher=cipher, curve=curve, data_to_send=random_bytes, insecure=True, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert # When the protocol is set to TLS13, the s2n server provider will default to using # all ciphers, not just the TLS13 ciphers. This is the desired behavior for this test. server_options.protocol = Protocols.TLS13 server = managed_process(S2N, server_options, timeout=5) client = managed_process(provider, client_options, timeout=5) server_version = get_expected_s2n_version(Protocols.TLS13, provider) actual_version = get_expected_s2n_version(protocol, provider) for results in client.get_results(): results.assert_success() if provider is S2N: # The client will get the server version from the SERVER HELLO, which will be the negotiated version assert to_bytes("Server protocol version: {}".format( actual_version)) in results.stdout assert to_bytes("Actual protocol version: {}".format( actual_version)) in results.stdout elif provider is OpenSSL: # This check cares about other providers because we want to know that they did negotiate the version # that our S2N server intended to negotiate. openssl_version = get_expected_openssl_version(protocol) assert to_bytes( "Protocol : {}".format(openssl_version)) in results.stdout for results in server.get_results(): results.assert_success() assert to_bytes("Server protocol version: {}".format( server_version)) in results.stdout assert to_bytes("Actual protocol version: {}".format( actual_version)) in results.stdout assert random_bytes in results.stdout
def test_s2n_new_server_old_ticket(managed_process, tmp_path, cipher, curve, protocol, provider, certificate): ticket_file = str(tmp_path / TICKET_FILE) assert not os.path.exists(ticket_file) options = ProviderOptions( port=next(available_ports), cipher=cipher, curve=curve, protocol=protocol, insecure=True, use_session_ticket=True, ) client_options = copy.copy(options) client_options.mode = Provider.ClientMode client_options.extra_flags = ['-sess_out', ticket_file] server_options = copy.copy(options) server_options.mode = Provider.ServerMode server_options.use_mainline_version = True server_options.key = certificate.key server_options.cert = certificate.cert server_options.data_to_send = CLOSE_MARKER_BYTES s2n_server = managed_process( S2N, server_options, send_marker=S2N.get_send_marker()) client = managed_process(provider, client_options, close_marker=str(CLOSE_MARKER_BYTES)) for results in client.get_results(): results.assert_success() for results in s2n_server.get_results(): results.assert_success() assert os.path.exists(ticket_file) client_options.extra_flags = ['-sess_in', ticket_file] server_options.use_mainline_version = False s2n_server = managed_process( S2N, server_options, send_marker=S2N.get_send_marker()) client = managed_process(provider, client_options, close_marker=str(CLOSE_MARKER_BYTES)) for results in client.get_results(): results.assert_success() for results in s2n_server.get_results(): results.assert_success() assert S2N_RESUMPTION_MARKER in results.stdout
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 test_s2n_client_signature_algorithms(managed_process, cipher, provider, protocol, certificate, signature, client_auth): port = next(available_ports) random_bytes = data_bytes(64) client_options = ProviderOptions( mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, data_to_send=random_bytes, insecure=True, use_client_auth=client_auth, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server_options.extra_flags=['-sigalgs', signature.name] if client_auth is True: client_options.client_trust_store = Certificates.RSA_2048_SHA256_WILDCARD.cert server_options.key = Certificates.RSA_2048_SHA256_WILDCARD.key server_options.cert = Certificates.RSA_2048_SHA256_WILDCARD.cert if signature.sig_type == 'RSA-PSS': client_options.client_trust_store = Certificates.RSA_PSS_2048_SHA256.cert server_options.key = Certificates.RSA_PSS_2048_SHA256.key server_options.cert = Certificates.RSA_PSS_2048_SHA256.cert elif signature.sig_type == 'ECDSA': client_options.client_trust_store = Certificates.ECDSA_256.cert server_options.key = Certificates.ECDSA_256.key server_options.cert = Certificates.ECDSA_256.cert server = managed_process(provider, server_options, timeout=5) client = managed_process(S2N, client_options, timeout=5) for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 assert bytes('Shared Signature Algorithms: {}+{}'.format(signature.sig_type, signature.sig_digest).encode('utf-8')) in results.stdout assert random_bytes in results.stdout expected_version = get_expected_s2n_version(protocol, provider) for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 assert bytes("Actual protocol version: {}".format(expected_version).encode('utf-8')) in results.stdout
def test_s2nc_tls13_negotiates_tls12(managed_process, cipher, curve, protocol, provider, certificate): port = next(available_ports) random_bytes = data_bytes(24) client_options = ProviderOptions(mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, curve=curve, data_to_send=random_bytes, insecure=True, protocol=Protocols.TLS13) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server_options.protocol = protocol server = managed_process(provider, server_options, timeout=5) client = managed_process(S2N, client_options, timeout=5) client_version = get_expected_s2n_version(Protocols.TLS13, provider) actual_version = get_expected_s2n_version(protocol, provider) for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 assert bytes("Client protocol version: {}".format( client_version).encode('utf-8')) in results.stdout assert bytes("Actual protocol version: {}".format( actual_version).encode('utf-8')) in results.stdout for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 if provider is S2N: # The server is only TLS12, so it reads the version from the CLIENT_HELLO, which is never above TLS12 # This check only cares about S2N. Trying to maintain expected output of other providers doesn't # add benefit to whether the S2N client was able to negotiate a lower TLS version. assert bytes("Client protocol version: {}".format( actual_version).encode('utf-8')) in results.stdout assert bytes("Actual protocol version: {}".format( actual_version).encode('utf-8')) in results.stdout assert random_bytes in results.stdout
def test_s2n_client_happy_path(managed_process, cipher, provider, curve, protocol, certificate): port = next(available_ports) # We can only send 4096 bytes here because of the way some servers chunk # output (when writing to stdout). If we send 8192 bytes, then openssl # will print some debugging information in the middle of our chunk. # We still want that debugging data in case of a failure, so we just # send less data, rather than lose debug information. random_bytes = data_bytes(4096) client_options = ProviderOptions(mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, curve=curve, data_to_send=random_bytes, insecure=True, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert # Passing the type of client and server as a parameter will # allow us to use a fixture to enumerate all possibilities. server = managed_process(provider, server_options, timeout=5) client = managed_process(S2N, client_options, timeout=5) expected_version = get_expected_s2n_version(protocol, provider) # The client is always S2N in this test, so we can examine # the stdout reliably. for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 assert bytes("Actual protocol version: {}".format( expected_version).encode('utf-8')) in results.stdout # The server will be one of all supported providers. We # just want to make sure there was no exception and that # the client exited cleanly. for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 # Avoid debugging information that sometimes gets inserted after the first character assert random_bytes[1:] in results.stdout
def test_s2n_client_with_early_data_rejected_via_hrr(managed_process, tmp_path, cipher, curve, certificate, protocol, provider, other_provider, early_data_size): if provider == S2N: pytest.skip( "S2N does not respect ProviderOptions.curve, so does not trigger a retry" ) early_data_file = str(tmp_path / EARLY_DATA_FILE) early_data = get_early_data_bytes(early_data_file, early_data_size) options = ProviderOptions( port=next(available_ports), cipher=cipher, curve=curve, protocol=protocol, insecure=True, use_session_ticket=True, reconnect=True, ) options.ticket_file = None options.early_data_file = early_data_file options.max_early_data = MAX_EARLY_DATA client_options = copy.copy(options) client_options.mode = Provider.ClientMode server_options = copy.copy(options) server_options.mode = Provider.ServerMode server_options.key = certificate.key # Required for the initial connection server_options.cert = certificate.cert # Required for the initial connection server_options.reconnects_before_exit = NUM_CONNECTIONS server = managed_process(provider, server_options, timeout=10) s2n_client = managed_process(S2N, client_options, timeout=10) for results in s2n_client.get_results(): results.assert_success() assert S2N_EARLY_DATA_MARKER not in results.stdout assert S2N_HRR_MARKER in results.stdout assert results.stdout.count( to_bytes(S2N_EARLY_DATA_REJECTED_MARKER)) == NUM_RESUMES for results in server.get_results(): results.assert_success() assert early_data not in results.stdout
def test_hrr_with_s2n_as_client(managed_process, cipher, provider, curve, protocol, certificate, keyshare): port = next(available_ports) random_bytes = data_bytes(64) client_options = ProviderOptions(mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, data_to_send=random_bytes, insecure=True, curve=curve, extra_flags=keyshare, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server_options.extra_flags = None server_options.curve = Curves.X25519 # Passing the type of client and server as a parameter will # allow us to use a fixture to enumerate all possibilities. server = managed_process(provider, server_options, timeout=5) client = managed_process(S2N, client_options, timeout=5) # The client should connect and return without error for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 assert bytes( "Curve: {}".format("x25519").encode('utf-8')) in results.stdout marker_part1 = b"cf 21 ad 74 e5" marker_part2 = b"9a 61 11 be 1d" for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 assert marker_part1 in results.stdout and marker_part2 in results.stdout if 'none' in keyshare: assert b'"key share" (id=51), len=2\n0000 - 00 00' in results.stdout assert b'Supported Elliptic Groups: X25519:P-256:P-384' in results.stdout assert bytes("Shared Elliptic groups: {}".format( server_options.curve).encode('utf-8')) in results.stdout assert random_bytes in results.stdout
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 test_sslyze_certificate_scans(managed_process, protocol, certificate, provider, certificate_scan): port = next(available_ports) server_options = ProviderOptions(mode=S2N.ServerMode, host=HOST, port=port, protocol=protocol, key=certificate.key, cert=certificate.cert, insecure=True, extra_flags=["--parallelize"]) server = managed_process(S2N, server_options, timeout=30) scan = { CertificateScan.CIPHER_SUITE_SCAN: CIPHER_SUITE_SCANS.get(protocol.value), CertificateScan.ELLIPTIC_CURVE_SCAN: sslyze.ScanCommand.ELLIPTIC_CURVES }.get(certificate_scan) scan_attempt_results = run_sslyze_scan(HOST, port, [scan]) for scan_attempt_result in scan_attempt_results: assert_scan_result_completed(scan_attempt_result) scan_result = scan_attempt_result.scan_result scan_attempts = get_scan_attempts(scan_result) for scan_attempt in scan_attempts: validate_scan_result(scan_attempt, protocol, certificate) server.kill()
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_sslyze_certificate_scans(managed_process, protocol, certificate): port = next(available_ports) server_options = ProviderOptions(mode=S2N.ServerMode, host=HOST, port=port, protocol=protocol, key=certificate.key, cert=certificate.cert, insecure=True, extra_flags=["--parallelize"]) server = managed_process(S2N, server_options, timeout=30) scans = [CIPHER_SUITE_SCANS.get(protocol.value)] # sslyze curves scan errors when given ECDSA certs if "ECDSA" not in certificate.name: scans.append(sslyze.ScanCommand.ELLIPTIC_CURVES) scan_attempt_results = run_sslyze_scan(HOST, port, scans) for scan_attempt_result in scan_attempt_results: assert_scan_result_completed(scan_attempt_result) scan_result = scan_attempt_result.scan_result scan_attempts = get_scan_attempts(scan_result) for scan_attempt in scan_attempts: validate_scan_result(scan_attempt, protocol, certificate) server.kill()
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 test_s2n_client_signature_algorithms(managed_process, cipher, provider, protocol, certificate, signature, client_auth): port = next(available_ports) random_bytes = data_bytes(64) client_options = ProviderOptions( mode=Provider.ClientMode, port=port, cipher=cipher, data_to_send=random_bytes, insecure=True, use_client_auth=client_auth, key=certificate.key, cert=certificate.cert, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server_options.trust_store = certificate.cert server_options.extra_flags=['-sigalgs', signature.name] server = managed_process(provider, server_options, timeout=5) client = managed_process(S2N, client_options, timeout=5) for results in server.get_results(): results.assert_success() assert random_bytes in results.stdout expected_version = get_expected_s2n_version(protocol, provider) # In versions before TLS1.3, the server uses the negotiated signature scheme for the # ServerKeyExchange message. The server only sends the ServerKeyExchange message when using # a key exchange method that provides forward secrecy, ie, NOT static RSA. # So if using RSA key exchange, there is no actual "negotiated" signature scheme, because # the server never sends the client a signature scheme. # # This mostly has to be inferred from the RFCs, but this blog post is a pretty good summary # of the situation: https://timtaubert.de/blog/2016/07/the-evolution-of-signatures-in-tls/ server_sigalg_used = not cipher.iana_standard_name.startswith("TLS_RSA_WITH_") for results in client.get_results(): results.assert_success() assert to_bytes("Actual protocol version: {}".format(expected_version)) in results.stdout assert signature_marker(Provider.ServerMode, signature) in results.stdout or not server_sigalg_used assert (signature_marker(Provider.ClientMode, signature) in results.stdout) == client_auth
def test_client_auth_with_s2n_server_using_nonmatching_certs(managed_process, cipher, provider, curve, protocol, certificate): host = "localhost" port = next(available_ports) client_options = ProviderOptions( mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, curve=curve, data_to_send=b'', use_client_auth=True, client_key_file=certificate.key, client_certificate_file=certificate.cert, insecure=False, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = Certificates.RSA_2048_SHA256_WILDCARD.key server_options.cert = Certificates.RSA_2048_SHA256_WILDCARD.cert # Tell the server to expect the wrong certificate server_options.client_certificate_file=Certificates.RSA_2048_SHA256_WILDCARD.cert # Passing the type of client and server as a parameter will # allow us to use a fixture to enumerate all possibilities. server = managed_process(S2N, server_options, timeout=5) client = managed_process(OpenSSL, client_options, timeout=5) # OpenSSL should return 1 because the connection failed for results in client.get_results(): assert results.exception is None if protocol == Protocols.TLS13: # Exit code 104 is connection reset by peer # This is almost always 104, but we have hit an occasion where s_client # closed cleanly. assert results.exit_code == 104 or results.exit_code == 0 else: assert results.exit_code == 1 # S2N should tell us that mutual authentication failed for results in server.get_results(): assert results.exception is None assert results.exit_code == 255 assert b'Error: Mutual Auth was required, but not negotiated' in results.stderr
def test_client_auth_with_s2n_server(managed_process, cipher, provider, protocol, certificate, client_certificate): port = next(available_ports) if protocol < Protocols.TLS12 and client_certificate.algorithm == 'EC': pytest.xfail( "Client auth with ECDSA certs is currently broken for versions < TLS1.2" ) random_bytes = data_bytes(64) client_options = ProviderOptions(mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, data_to_send=random_bytes, use_client_auth=True, key=client_certificate.key, cert=client_certificate.cert, trust_store=certificate.cert, insecure=False, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server_options.trust_store = client_certificate.cert server = managed_process(S2N, server_options, timeout=5) client = managed_process(provider, client_options, timeout=5) # Openssl should send a client certificate and complete the handshake for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 assert b'write client certificate' in results.stderr assert b'write certificate verify' in results.stderr assert_openssl_handshake_complete(results) # S2N should successfully connect for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 assert_s2n_handshake_complete(results, protocol, provider) assert random_bytes in results.stdout
def test_s2n_old_client_new_ticket(managed_process, tmp_path, cipher, curve, certificate, protocol, provider, other_provider): ticket_file = str(tmp_path / TICKET_FILE) assert not os.path.exists(ticket_file) options = ProviderOptions( port=next(available_ports), cipher=cipher, curve=curve, protocol=protocol, insecure=True, use_session_ticket=True, ) client_options = copy.copy(options) client_options.mode = Provider.ClientMode client_options.extra_flags = ['--ticket-out', ticket_file] server_options = copy.copy(options) server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server = managed_process(provider, server_options) s2n_client = managed_process(S2N, client_options) for results in s2n_client.get_results(): results.assert_success() for results in server.get_results(): results.assert_success() assert os.path.exists(ticket_file) client_options.extra_flags = ['--ticket-in', ticket_file] client_options.use_mainline_version = True server = managed_process(provider, server_options) s2n_client = managed_process(other_provider, client_options) for results in s2n_client.get_results(): results.assert_success() assert S2N_RESUMPTION_MARKER in results.stdout for results in server.get_results(): results.assert_success() assert S2N_RESUMPTION_MARKER in results.stdout
def test_s2n_client_dynamic_record(custom_mtu, managed_process, cipher, curve, provider, protocol, certificate): host = "localhost" port = next(available_ports) # 16384 bytes is enough to reliably get a packet that will exceed the MTU bytes_to_send = data_bytes(16384) client_options = ProviderOptions(mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, data_to_send=bytes_to_send, insecure=True, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert expected_version = get_expected_s2n_version(protocol, provider) # This test shouldn't last longer than 5 seconds, even though # Tcpdump tends to take a second to startup. tcpdump = managed_process(Tcpdump, client_options, timeout=5) server = managed_process(provider, server_options, timeout=5) client = managed_process(S2N, client_options, timeout=5) for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 assert bytes("Actual protocol version: {}".format( expected_version).encode('utf-8')) in results.stdout for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 # The Tcpdump provider only captures 12 packets. This is enough # to detect a packet larger than the MTU, but less than the # total packets sent. This is important because it lets Tcpdump # exit cleanly, which means all the output is available for us # to examine. for results in tcpdump.get_results(): assert results.exit_code == 0 assert find_fragmented_packet(results.stdout) is True
def test_client_auth_with_s2n_server(managed_process, cipher, provider, curve, protocol, certificate): host = "localhost" port = next(available_ports) # NOTE: Client Auth is failing for ECDSA client certs if 'ecdsa' in certificate.cert: pytest.skip("Skipping known failure, ECDSA client auth certificate") random_bytes = data_bytes(64) client_options = ProviderOptions( mode=Provider.ClientMode, host="localhost", port=port, cipher=cipher, curve=curve, data_to_send=random_bytes, use_client_auth=True, client_key_file=certificate.key, client_certificate_file=certificate.cert, insecure=False, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = "../pems/rsa_2048_sha256_wildcard_key.pem" server_options.cert = "../pems/rsa_2048_sha256_wildcard_cert.pem" # Passing the type of client and server as a parameter will # allow us to use a fixture to enumerate all possibilities. server = managed_process(S2N, server_options, timeout=5) client = managed_process(provider, client_options, timeout=5) # The client should connect and return without error for results in client.get_results(): assert results.exception is None assert results.exit_code == 0 expected_version = get_expected_s2n_version(protocol, provider) # S2N should indicate the procotol version in a successful connection. for results in server.get_results(): assert results.exception is None assert results.exit_code == 0 assert bytes("Actual protocol version: {}".format(expected_version).encode('utf-8')) in results.stdout assert random_bytes in results.stdout
def test_s2n_server_framented_data(managed_process, cipher, provider, other_provider, protocol, certificate, frag_len): if provider is OpenSSL and 'openssl-1.0.2' in provider.get_version(): pytest.skip( '{} does not allow setting max fragmentation for packets'.format( provider)) port = next(available_ports) random_bytes = data_bytes(65519) client_options = ProviderOptions(mode=Provider.ClientMode, port=port, cipher=cipher, data_to_send=random_bytes, insecure=True, record_size=frag_len, protocol=protocol) server_options = copy.copy(client_options) server_options.extra_flags = None server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server_options.cipher = None server = managed_process(S2N, server_options, timeout=5) client = managed_process(provider, client_options, timeout=5) for client_results in client.get_results(): client_results.assert_success() expected_version = get_expected_s2n_version(protocol, provider) for server_results in server.get_results(): server_results.assert_success() assert to_bytes("Actual protocol version: {}".format( expected_version)) in server_results.stdout if provider == GnuTLS: # GnuTLS ignores data sent through stdin past frag_len up to the application data # packet length of 4096. so, just check to make sure data up to frag_len was received. assert random_bytes[:frag_len] in server_results.stdout else: assert random_bytes in server_results.stdout
def test_s2n_server_signature_algorithms(managed_process, cipher, provider, protocol, certificate, signature, client_auth): port = next(available_ports) random_bytes = data_bytes(64) client_options = ProviderOptions(mode=Provider.ClientMode, port=port, cipher=cipher, data_to_send=random_bytes, insecure=False, use_client_auth=client_auth, key=certificate.key, cert=certificate.cert, signature_algorithm=signature, protocol=protocol) if provider == GnuTLS: # GnuTLS fails the CA verification. It must be run with this check disabled. client_options.extra_flags = ["--no-ca-verification"] server_options = copy.copy(client_options) server_options.extra_flags = None server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server = managed_process(S2N, server_options, timeout=5) client = managed_process(provider, client_options, timeout=5) for results in client.get_results(): results.assert_success() expected_version = get_expected_s2n_version(protocol, provider) for results in server.get_results(): results.assert_success() assert to_bytes("Actual protocol version: {}".format( expected_version)) in results.stdout assert signature_marker(Provider.ServerMode, signature) in results.stdout assert (signature_marker(Provider.ClientMode, signature) in results.stdout) == client_auth assert random_bytes in results.stdout
def test_hrr_with_s2n_as_client(managed_process, cipher, provider, curve, protocol, certificate): if curve == S2N_DEFAULT_CURVE: pytest.skip("No retry if server curve matches client curve") port = next(available_ports) random_bytes = data_bytes(64) client_options = ProviderOptions(mode=Provider.ClientMode, port=port, cipher=cipher, data_to_send=random_bytes, insecure=True, protocol=protocol) server_options = copy.copy(client_options) server_options.data_to_send = None server_options.mode = Provider.ServerMode server_options.key = certificate.key server_options.cert = certificate.cert server_options.extra_flags = None server_options.curve = curve # Passing the type of client and server as a parameter will # allow us to use a fixture to enumerate all possibilities. server = managed_process(provider, server_options, timeout=5) client = managed_process(S2N, client_options, timeout=5) # The client should connect and return without error for results in client.get_results(): results.assert_success() assert to_bytes("Curve: {}".format( CURVE_NAMES[curve.name])) in results.stdout assert S2N_HRR_MARKER in results.stdout marker_part1 = b"cf 21 ad 74 e5" marker_part2 = b"9a 61 11 be 1d" for results in server.get_results(): results.assert_success() assert marker_part1 in results.stdout and marker_part2 in results.stdout assert b'Supported Elliptic Groups: X25519:P-256:P-384' in results.stdout assert to_bytes("Shared Elliptic groups: {}".format( server_options.curve)) in results.stdout assert random_bytes in results.stdout