예제 #1
0
def test_good_authenticated_handshake(alice):
    sh = ServerHandshake()

    ch = AuthenticatedClientHandshake(
        alice.organization_id, alice.device_id, alice.signing_key, alice.root_verify_key
    )
    assert sh.state == "stalled"

    challenge_req = sh.build_challenge_req()
    assert sh.state == "challenge"

    answer_req = ch.process_challenge_req(challenge_req)

    sh.process_answer_req(answer_req)
    assert sh.state == "answer"
    assert sh.answer_type == HandshakeType.AUTHENTICATED
    assert sh.answer_data == {
        "answer": ANY,
        "client_api_version": API_V2_VERSION,
        "organization_id": alice.organization_id,
        "device_id": alice.device_id,
        "rvk": alice.root_verify_key,
    }
    result_req = sh.build_result_req(alice.verify_key)
    assert sh.state == "result"

    ch.process_result_req(result_req)
    assert sh.client_api_version == API_V2_VERSION
예제 #2
0
    async def _backend_sock_factory(backend, auth_as: LocalDevice, freeze_on_transport_error=True):
        async with backend_raw_transport_factory(
            backend, freeze_on_transport_error=freeze_on_transport_error
        ) as transport:
            # Handshake
            ch = AuthenticatedClientHandshake(
                auth_as.organization_id,
                auth_as.device_id,
                auth_as.signing_key,
                auth_as.root_verify_key,
            )
            challenge_req = await transport.recv()
            answer_req = ch.process_challenge_req(challenge_req)
            await transport.send(answer_req)
            result_req = await transport.recv()
            ch.process_result_req(result_req)

            yield transport
예제 #3
0
async def test_authenticated_handshake_bad_rvk(backend, server_factory, alice,
                                               otherorg):
    ch = AuthenticatedClientHandshake(
        organization_id=alice.organization_id,
        device_id=alice.device_id,
        user_signkey=alice.signing_key,
        root_verify_key=otherorg.root_verify_key,
    )
    async with server_factory(backend.handle_client) as server:
        stream = await server.connection_factory()
        transport = await Transport.init_for_client(stream, "127.0.0.1")

        challenge_req = await transport.recv()
        answer_req = ch.process_challenge_req(challenge_req)

        await transport.send(answer_req)
        result_req = await transport.recv()
        with pytest.raises(HandshakeRVKMismatch):
            ch.process_result_req(result_req)
예제 #4
0
async def test_authenticated_handshake_unknown_device(backend, server_factory,
                                                      mallory):
    ch = AuthenticatedClientHandshake(
        organization_id=mallory.organization_id,
        device_id=mallory.device_id,
        user_signkey=mallory.signing_key,
        root_verify_key=mallory.root_verify_key,
    )
    async with server_factory(backend.handle_client) as server:
        stream = await server.connection_factory()
        transport = await Transport.init_for_client(stream, "127.0.0.1")

        challenge_req = await transport.recv()
        answer_req = ch.process_challenge_req(challenge_req)

        await transport.send(answer_req)
        result_req = await transport.recv()
        with pytest.raises(HandshakeBadIdentity):
            ch.process_result_req(result_req)
예제 #5
0
async def test_authenticated_handshake_good(backend, server_factory, alice):
    ch = AuthenticatedClientHandshake(
        organization_id=alice.organization_id,
        device_id=alice.device_id,
        user_signkey=alice.signing_key,
        root_verify_key=alice.root_verify_key,
    )

    async with server_factory(backend.handle_client) as server:
        stream = await server.connection_factory()
        transport = await Transport.init_for_client(stream, "127.0.0.1")

        challenge_req = await transport.recv()
        answer_req = ch.process_challenge_req(challenge_req)

        await transport.send(answer_req)
        result_req = await transport.recv()
        ch.process_result_req(result_req)

        assert ch.client_api_version == API_VERSION
        assert ch.backend_api_version == API_VERSION
예제 #6
0
async def test_handle_client_coroutine_destroyed_on_client_left(
        backend, alice, close_on, clean_close, recwarn):
    # For this test we want to use a real TCP socket (instead of relying on
    # the `tcp_stream_spy` mock fixture) test the backend on

    outcome = None
    outcome_available = trio.Event()

    async def _handle_client_with_captured_outcome(stream):
        nonlocal outcome
        try:
            ret = await backend.handle_client(stream)
        except BaseException as exc:
            outcome = ("exception", exc)
            outcome_available.set()
            raise
        else:
            outcome = ("return", ret)
            outcome_available.set()
            return ret

    async with trio.open_nursery() as nursery:
        try:
            # Start server
            listeners = await nursery.start(
                trio.serve_tcp, _handle_client_with_captured_outcome, 0)

            # Client connect to the server
            client_stream = await open_stream_to_socket_listener(listeners[0])

            async def _do_close_client():
                if clean_close:
                    await client_stream.aclose()
                else:
                    # Reset the tcp socket instead of regular clean close
                    # See https://stackoverflow.com/a/54065411
                    l_onoff = 1
                    l_linger = 0
                    client_stream.setsockopt(
                        socket.SOL_SOCKET, socket.SO_LINGER,
                        struct.pack("ii", l_onoff, l_linger))
                    client_stream.socket.close()

                with trio.fail_after(1):
                    await outcome_available.wait()

            if close_on == "tcp_ready":
                await _do_close_client()

            else:
                if close_on == "before_http_request":
                    # Send the beginning of an http request
                    await client_stream.send_all(b"GET / HTTP/1.1\r\n")
                    await _do_close_client()

                elif close_on in ("after_http_request"):
                    # Send an entire http request
                    await client_stream.send_all(b"GET / HTTP/1.0\r\n\r\n")
                    # Peer will realize connection is closed after having sent
                    # the answer for the previous request
                    await _do_close_client()

                else:
                    # First request doing websocket negotiation
                    hostname = f"127.0.0.1:{listeners[0].socket.getsockname()}"
                    transport = await Transport.init_for_client(
                        client_stream, hostname)

                    if close_on == "websocket_ready":
                        await _do_close_client()
                    else:
                        # Client do the handshake
                        ch = AuthenticatedClientHandshake(
                            alice.organization_id,
                            alice.device_id,
                            alice.signing_key,
                            alice.root_verify_key,
                        )
                        challenge_req = await transport.recv()
                        answer_req = ch.process_challenge_req(challenge_req)

                        if close_on == "handshake_started":
                            await _do_close_client()

                        else:
                            await transport.send(answer_req)
                            result_req = await transport.recv()
                            ch.process_result_req(result_req)

                            assert close_on == "handshake_done"  # Sanity check
                            await _do_close_client()

            # Outcome should aways be the same
            assert outcome == ("return", None)

        finally:
            nursery.cancel_scope.cancel()