예제 #1
0
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
예제 #2
0
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
예제 #3
0
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
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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)
예제 #8
0
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
예제 #9
0
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
예제 #10
0
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
예제 #11
0
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)
예제 #12
0
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
예제 #13
0
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
예제 #14
0
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
예제 #15
0
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
예제 #16
0
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
예제 #17
0
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
예제 #18
0
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()
예제 #19
0
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
예제 #20
0
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()
예제 #21
0
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
예제 #23
0
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
예제 #24
0
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
예제 #25
0
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
예제 #27
0
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
예제 #28
0
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
예제 #29
0
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