Example #1
0
        def new_user_and_device(self, is_admin, certifier_id, certifier_key):
            device_id = self.next_device_id()

            local_device = local_device_factory(device_id, org=coolorg)
            self.local_devices[device_id] = local_device

            user = UserCertificateContent(
                author=certifier_id,
                timestamp=pendulum_now(),
                user_id=local_device.user_id,
                public_key=local_device.public_key,
                is_admin=is_admin,
            )
            self.users_content[device_id.user_id] = user
            self.users_certifs[device_id.user_id] = user.dump_and_sign(
                certifier_key)

            device = DeviceCertificateContent(
                author=certifier_id,
                timestamp=pendulum_now(),
                device_id=local_device.device_id,
                verify_key=local_device.verify_key,
            )
            self.devices_content[local_device.device_id] = device
            self.devices_certifs[
                local_device.device_id] = device.dump_and_sign(certifier_key)

            return device_id
Example #2
0
def _create_new_user_certificates(
    author: LocalDevice,
    device_label: Optional[DeviceLabel],
    human_handle: Optional[HumanHandle],
    profile: UserProfile,
    public_key: PublicKey,
    verify_key: VerifyKey,
) -> Tuple[bytes, bytes, bytes, bytes, InviteUserConfirmation]:
    """Helper to prepare the creation of a new user."""
    device_id = DeviceID.new()
    try:
        timestamp = author.timestamp()

        user_certificate = UserCertificateContent(
            author=author.device_id,
            timestamp=timestamp,
            user_id=device_id.user_id,
            human_handle=human_handle,
            public_key=public_key,
            profile=profile,
        )
        redacted_user_certificate = user_certificate.evolve(human_handle=None)

        device_certificate = DeviceCertificateContent(
            author=author.device_id,
            timestamp=timestamp,
            device_id=device_id,
            device_label=device_label,
            verify_key=verify_key,
        )
        redacted_device_certificate = device_certificate.evolve(
            device_label=None)

        user_certificate = user_certificate.dump_and_sign(author.signing_key)
        redacted_user_certificate = redacted_user_certificate.dump_and_sign(
            author.signing_key)
        device_certificate = device_certificate.dump_and_sign(
            author.signing_key)
        redacted_device_certificate = redacted_device_certificate.dump_and_sign(
            author.signing_key)

    except DataError as exc:
        raise InviteError(
            f"Cannot generate device certificate: {exc}") from exc

    invite_user_confirmation = InviteUserConfirmation(
        device_id=device_id,
        device_label=device_label,
        human_handle=human_handle,
        profile=profile,
        root_verify_key=author.root_verify_key,
    )

    return (
        user_certificate,
        redacted_user_certificate,
        device_certificate,
        redacted_device_certificate,
        invite_user_confirmation,
    )
        def new_user_and_device(self, is_admin, certifier_id, certifier_key):
            device_id = self.next_device_id()

            local_device = local_device_factory(device_id, org=coolorg)
            self.local_devices[device_id] = local_device

            user = UserCertificateContent(
                author=certifier_id,
                timestamp=pendulum_now(),
                user_id=local_device.user_id,
                human_handle=local_device.human_handle,
                public_key=local_device.public_key,
                profile=UserProfile.ADMIN
                if is_admin else UserProfile.STANDARD,
            )
            self.users_content[device_id.user_id] = user
            self.users_certifs[device_id.user_id] = user.dump_and_sign(
                certifier_key)

            device = DeviceCertificateContent(
                author=certifier_id,
                timestamp=pendulum_now(),
                device_id=local_device.device_id,
                device_label=local_device.device_label,
                verify_key=local_device.verify_key,
            )
            self.devices_content[local_device.device_id] = device
            self.devices_certifs[
                local_device.device_id] = device.dump_and_sign(certifier_key)

            return device_id
Example #4
0
    async def api_device_create(self, client_ctx, msg):
        msg = device_create_serializer.req_load(msg)

        try:
            data = DeviceCertificateContent.verify_and_load(
                msg["device_certificate"],
                author_verify_key=client_ctx.verify_key,
                expected_author=client_ctx.device_id,
            )

            redacted_data = None
            if msg["redacted_device_certificate"]:
                redacted_data = DeviceCertificateContent.verify_and_load(
                    msg["redacted_device_certificate"],
                    author_verify_key=client_ctx.verify_key,
                    expected_author=client_ctx.device_id,
                )

        except DataError as exc:
            return {
                "status": "invalid_certification",
                "reason": f"Invalid certification data ({exc}).",
            }

        if not timestamps_in_the_ballpark(data.timestamp, pendulum.now()):
            return {
                "status": "invalid_certification",
                "reason": f"Invalid timestamp in certification.",
            }

        if data.device_id.user_id != client_ctx.user_id:
            return {"status": "bad_user_id", "reason": "Device must be handled by it own user."}

        if redacted_data:
            if redacted_data.evolve(device_label=data.device_label) != data:
                return {
                    "status": "invalid_data",
                    "reason": "Redacted Device certificate differs from Device certificate.",
                }
            if redacted_data.device_label:
                return {
                    "status": "invalid_data",
                    "reason": "Redacted Device certificate must not contain a device_label field.",
                }

        try:
            device = Device(
                device_id=data.device_id,
                device_label=data.device_label,
                device_certificate=msg["device_certificate"],
                redacted_device_certificate=msg["redacted_device_certificate"]
                or msg["device_certificate"],
                device_certifier=data.author,
                created_on=data.timestamp,
            )
            await self.create_device(client_ctx.organization_id, device)
        except UserAlreadyExistsError as exc:
            return {"status": "already_exists", "reason": str(exc)}

        return device_create_serializer.rep_dump({"status": "ok"})
Example #5
0
async def bootstrap_organization(
    cmds: APIV1_BackendAnonymousCmds,
    human_handle: Optional[HumanHandle],
    device_label: Optional[str],
) -> LocalDevice:
    root_signing_key = SigningKey.generate()
    root_verify_key = root_signing_key.verify_key

    organization_addr = BackendOrganizationAddr.build(
        backend_addr=cmds.addr,
        organization_id=cmds.addr.organization_id,
        root_verify_key=root_verify_key,
    )

    device = generate_new_device(
        organization_addr=organization_addr,
        profile=UserProfile.ADMIN,
        human_handle=human_handle,
        device_label=device_label,
    )

    now = pendulum_now()
    user_certificate = UserCertificateContent(
        author=None,
        timestamp=now,
        user_id=device.user_id,
        human_handle=device.human_handle,
        public_key=device.public_key,
        profile=device.profile,
    )
    redacted_user_certificate = user_certificate.evolve(human_handle=None)
    device_certificate = DeviceCertificateContent(
        author=None,
        timestamp=now,
        device_id=device.device_id,
        device_label=device.device_label,
        verify_key=device.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)

    user_certificate = user_certificate.dump_and_sign(root_signing_key)
    redacted_user_certificate = redacted_user_certificate.dump_and_sign(
        root_signing_key)
    device_certificate = device_certificate.dump_and_sign(root_signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(
        root_signing_key)

    rep = await cmds.organization_bootstrap(
        organization_id=cmds.addr.organization_id,
        bootstrap_token=cmds.addr.token,
        root_verify_key=root_verify_key,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=redacted_user_certificate,
        redacted_device_certificate=redacted_device_certificate,
    )
    _check_rep(rep, step_name="organization bootstrap")

    return device
Example #6
0
    async def do_create_new_device(self, author: LocalDevice,
                                   device_label: Optional[str]) -> None:
        device_id = DeviceID(f"{author.user_id}@{DeviceName.new()}")
        try:
            now = pendulum_now()

            device_certificate = DeviceCertificateContent(
                author=author.device_id,
                timestamp=now,
                device_id=device_id,
                verify_key=self._verify_key,
                device_label=device_label,
            )
            redacted_device_certificate = device_certificate.evolve(
                device_label=None)

            device_certificate = device_certificate.dump_and_sign(
                author.signing_key)
            redacted_device_certificate = redacted_device_certificate.dump_and_sign(
                author.signing_key)

        except DataError as exc:
            raise InviteError(
                f"Cannot generate device certificate: {exc}") from exc

        rep = await self._cmds.device_create(
            device_certificate=device_certificate,
            redacted_device_certificate=redacted_device_certificate,
        )
        if rep["status"] != "ok":
            raise InviteError(f"Cannot create device: {rep}")

        try:
            payload = InviteDeviceConfirmation(
                device_id=device_id,
                device_label=device_label,
                human_handle=author.human_handle,
                profile=author.profile,
                private_key=author.private_key,
                user_manifest_id=author.user_manifest_id,
                user_manifest_key=author.user_manifest_key,
                root_verify_key=author.root_verify_key,
            ).dump_and_encrypt(key=self._shared_secret_key)
        except DataError as exc:
            raise InviteError(
                "Cannot generate InviteUserConfirmation payload") from exc

        rep = await self._cmds.invite_4_greeter_communicate(token=self.token,
                                                            payload=payload)
        if rep["status"] in ("not_found", "already_deleted"):
            raise InviteNotAvailableError()
        elif rep["status"] == "invalid_state":
            raise InvitePeerResetError()
        elif rep["status"] != "ok":
            raise InviteError(
                f"Backend error during step 4 (confirmation exchange): {rep}")

        await self._cmds.invite_delete(token=self.token,
                                       reason=InvitationDeletedReason.FINISHED)
Example #7
0
async def bootstrap_organization(
    addr: BackendOrganizationBootstrapAddr,
    human_handle: Optional[HumanHandle],
    device_label: Optional[DeviceLabel],
) -> LocalDevice:
    root_signing_key = SigningKey.generate()
    root_verify_key = root_signing_key.verify_key

    organization_addr = BackendOrganizationAddr.build(
        backend_addr=addr.get_backend_addr(),
        organization_id=addr.organization_id,
        root_verify_key=root_verify_key,
    )

    device = generate_new_device(
        organization_addr=organization_addr,
        profile=UserProfile.ADMIN,
        human_handle=human_handle,
        device_label=device_label,
    )

    timestamp = device.timestamp()
    user_certificate = UserCertificateContent(
        author=None,
        timestamp=timestamp,
        user_id=device.user_id,
        human_handle=device.human_handle,
        public_key=device.public_key,
        profile=device.profile,
    )
    redacted_user_certificate = user_certificate.evolve(human_handle=None)
    device_certificate = DeviceCertificateContent(
        author=None,
        timestamp=timestamp,
        device_id=device.device_id,
        device_label=device.device_label,
        verify_key=device.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)

    user_certificate = user_certificate.dump_and_sign(root_signing_key)
    redacted_user_certificate = redacted_user_certificate.dump_and_sign(root_signing_key)
    device_certificate = device_certificate.dump_and_sign(root_signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(root_signing_key)

    rep = await failsafe_organization_bootstrap(
        addr=addr,
        root_verify_key=root_verify_key,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=redacted_user_certificate,
        redacted_device_certificate=redacted_device_certificate,
    )
    _check_rep(rep, step_name="organization bootstrap")

    return device
async def test_redacted_certificates_cannot_contain_sensitive_data(
        alice_backend_sock, alice, mallory):
    now = pendulum.now()
    user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=now,
        user_id=mallory.user_id,
        public_key=mallory.public_key,
        profile=UserProfile.STANDARD,
        human_handle=mallory.human_handle,
    )
    redacted_user_certificate = user_certificate.evolve(human_handle=None)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=mallory.device_id,
        verify_key=mallory.verify_key,
        device_label=mallory.device_label,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)

    user_certificate = user_certificate.dump_and_sign(alice.signing_key)
    device_certificate = device_certificate.dump_and_sign(alice.signing_key)
    redacted_user_certificate = redacted_user_certificate.dump_and_sign(
        alice.signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(
        alice.signing_key)

    with freeze_time(now):
        rep = await user_create(
            alice_backend_sock,
            user_certificate=user_certificate,
            device_certificate=device_certificate,
            redacted_user_certificate=user_certificate,
            redacted_device_certificate=redacted_device_certificate,
        )
        assert rep == {
            "status":
            "invalid_data",
            "reason":
            "Redacted User certificate must not contain a human_handle field.",
        }

        rep = await user_create(
            alice_backend_sock,
            user_certificate=user_certificate,
            device_certificate=device_certificate,
            redacted_user_certificate=redacted_user_certificate,
            redacted_device_certificate=device_certificate,
        )
        assert rep == {
            "status":
            "invalid_data",
            "reason":
            "Redacted Device certificate must not contain a device_label field.",
        }
Example #9
0
async def test_user_create_nok_active_users_limit_reached(
    backend, backend_data_binder_factory, backend_sock_factory, coolorg, alice, mallory
):
    # Ensure there is only one user in the organization...
    binder = backend_data_binder_factory(backend)
    await binder.bind_organization(coolorg, alice)
    # ...so our active user limit has just been reached
    await backend.organization.update(alice.organization_id, active_users_limit=1)

    now = pendulum.now()
    user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=now,
        user_id=mallory.user_id,
        human_handle=None,
        public_key=mallory.public_key,
        profile=UserProfile.STANDARD,
    )
    redacted_user_certificate = user_certificate.evolve(human_handle=None)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=mallory.device_id,
        device_label=None,
        verify_key=mallory.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)

    user_certificate = user_certificate.dump_and_sign(alice.signing_key)
    device_certificate = device_certificate.dump_and_sign(alice.signing_key)
    redacted_user_certificate = redacted_user_certificate.dump_and_sign(alice.signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(alice.signing_key)

    async with backend_sock_factory(backend, alice) as sock:
        rep = await user_create(
            sock,
            user_certificate=user_certificate,
            device_certificate=device_certificate,
            redacted_user_certificate=redacted_user_certificate,
            redacted_device_certificate=redacted_device_certificate,
        )
        assert rep == {"status": "active_users_limit_reached"}

        # Now correct the limit, and ensure the user can be created

        await backend.organization.update(alice.organization_id, active_users_limit=2)

        rep = await user_create(
            sock,
            user_certificate=user_certificate,
            device_certificate=device_certificate,
            redacted_user_certificate=redacted_user_certificate,
            redacted_device_certificate=redacted_device_certificate,
        )
        assert rep == {"status": "ok"}
Example #10
0
async def test_device_claim_invalid_returned_certificate(
        running_backend, backend, alice, bob, backend_claim_response_hook):
    hooks = backend_claim_response_hook

    device_count = 0

    async def _do_test():
        nonlocal device_count
        device_count += 1
        new_device_id = DeviceID(f"{alice.user_id}@newdev{device_count}")
        token = generate_invitation_token()
        exception_occured = False

        async def _from_alice():
            await invite_and_create_device(alice,
                                           new_device_id.device_name,
                                           token=token)

        async def _from_new_device():
            nonlocal exception_occured
            with pytest.raises(InviteClaimCryptoError):
                await claim_device(alice.organization_addr,
                                   new_device_id,
                                   token=token)
            exception_occured = True

        await _invite_and_claim(running_backend,
                                _from_alice,
                                _from_new_device,
                                event_name="device.claimed")
        assert exception_occured

    # Invalid data

    hooks["device_certificate"] = lambda x: b"dummy"
    await _do_test()

    # Certificate author differs from invitation creator

    def bob_sign(certif):
        return certif.evolve(author=bob.device_id).dump_and_sign(
            author_signkey=bob.signing_key)

    hooks["device_certificate"] = lambda raw: bob_sign(
        DeviceCertificateContent.unsecure_load(raw))
    await _do_test()

    # Certificate info doesn't correspond to created user

    hooks["device_certificate"] = (
        lambda raw: DeviceCertificateContent.unsecure_load(raw).evolve(
            device_id=bob.device_id).dump_and_sign(author_signkey=alice.
                                                   signing_key))
    await _do_test()
async def _bootstrap_organization(debug, device_id,
                                  organization_bootstrap_addr, config_dir,
                                  force, password):
    root_signing_key = SigningKey.generate()
    root_verify_key = root_signing_key.verify_key
    organization_addr = organization_bootstrap_addr.generate_organization_addr(
        root_verify_key)

    device_display = click.style(device_id, fg="yellow")
    device = generate_new_device(device_id,
                                 organization_addr,
                                 profile=UserProfile.ADMIN)

    with operation(f"Creating locally {device_display}"):
        save_device_with_password(config_dir, device, password, force=force)

    now = pendulum.now()
    user_certificate = UserCertificateContent(
        author=None,
        timestamp=now,
        user_id=device.user_id,
        public_key=device.public_key,
        profile=device.profile,
    )
    redacted_user_certificate = user_certificate.evolve(human_handle=None)
    device_certificate = DeviceCertificateContent(author=None,
                                                  timestamp=now,
                                                  device_id=device_id,
                                                  verify_key=device.verify_key)
    redacted_device_certificate = device_certificate.evolve(device_label=None)

    user_certificate = user_certificate.dump_and_sign(root_signing_key)
    device_certificate = device_certificate.dump_and_sign(root_signing_key)
    redacted_user_certificate = redacted_user_certificate.dump_and_sign(
        root_signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(
        root_signing_key)

    async with spinner(f"Sending {device_display} to server"):
        async with apiv1_backend_anonymous_cmds_factory(
                organization_bootstrap_addr) as cmds:
            await cmds.organization_bootstrap(
                organization_id=organization_bootstrap_addr.organization_id,
                bootstrap_token=organization_bootstrap_addr.token,
                root_verify_key=root_verify_key,
                user_certificate=user_certificate,
                device_certificate=device_certificate,
                redacted_user_certificate=redacted_user_certificate,
                redacted_device_certificate=redacted_device_certificate,
            )

    organization_addr_display = click.style(organization_addr.to_url(),
                                            fg="yellow")
    click.echo(f"Organization url: {organization_addr_display}")
Example #12
0
async def test_device_create_ok(backend, backend_sock_factory,
                                alice_backend_sock, alice, alice_nd,
                                with_labels):
    now = pendulum.now()
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=alice_nd.device_id,
        device_label=alice_nd.device_label,
        verify_key=alice_nd.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)
    if not with_labels:
        device_certificate = redacted_device_certificate
    device_certificate = device_certificate.dump_and_sign(alice.signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(
        alice.signing_key)

    with backend.event_bus.listen() as spy:
        rep = await device_create(
            alice_backend_sock,
            device_certificate=device_certificate,
            redacted_device_certificate=redacted_device_certificate,
        )
        assert rep == {"status": "ok"}

        # No guarantees this event occurs before the command's return
        await spy.wait_with_timeout(
            BackendEvent.DEVICE_CREATED,
            {
                "organization_id": alice_nd.organization_id,
                "device_id": alice_nd.device_id,
                "device_certificate": device_certificate,
                "encrypted_answer": b"",
            },
        )

    # Make sure the new device can connect now
    async with backend_sock_factory(backend, alice_nd) as sock:
        rep = await ping(sock, ping="Hello world !")
        assert rep == {"status": "ok", "pong": "Hello world !"}

    # Check the resulting data in the backend
    _, backend_device = await backend.user.get_user_with_device(
        alice_nd.organization_id, alice_nd.device_id)
    assert backend_device == Device(
        device_id=alice_nd.device_id,
        device_label=alice_nd.device_label if with_labels else None,
        device_certificate=device_certificate,
        redacted_device_certificate=redacted_device_certificate,
        device_certifier=alice.device_id,
        created_on=now,
    )
Example #13
0
def local_device_to_backend_user(
    device: LocalDevice, certifier: Union[LocalDevice, OrganizationFullData]
) -> Tuple[BackendUser, BackendDevice]:
    if isinstance(certifier, OrganizationFullData):
        certifier_id = None
        certifier_signing_key = certifier.root_signing_key
    else:
        certifier_id = certifier.device_id
        certifier_signing_key = certifier.signing_key

    timestamp = device.timestamp()

    user_certificate = UserCertificateContent(
        author=certifier_id,
        timestamp=timestamp,
        user_id=device.user_id,
        public_key=device.public_key,
        profile=device.profile,
        human_handle=device.human_handle,
    )
    device_certificate = DeviceCertificateContent(
        author=certifier_id,
        timestamp=timestamp,
        device_id=device.device_id,
        device_label=device.device_label,
        verify_key=device.verify_key,
    )
    redacted_user_certificate = user_certificate.evolve(human_handle=None)
    redacted_device_certificate = device_certificate.evolve(device_label=None)

    user = BackendUser(
        user_id=device.user_id,
        human_handle=device.human_handle,
        profile=device.profile,
        user_certificate=user_certificate.dump_and_sign(certifier_signing_key),
        redacted_user_certificate=redacted_user_certificate.dump_and_sign(
            certifier_signing_key),
        user_certifier=certifier_id,
        created_on=timestamp,
    )

    first_device = BackendDevice(
        device_id=device.device_id,
        device_label=device.device_label,
        device_certificate=device_certificate.dump_and_sign(
            certifier_signing_key),
        redacted_device_certificate=redacted_device_certificate.dump_and_sign(
            certifier_signing_key),
        device_certifier=certifier_id,
        created_on=timestamp,
    )

    return user, first_device
Example #14
0
    async def do_create_new_device(self, author: LocalDevice,
                                   device_label: Optional[str]) -> None:
        device_id = author.user_id.to_device_id(DeviceName.new())
        try:
            now = pendulum_now()

            device_certificate = DeviceCertificateContent(
                author=author.device_id,
                timestamp=now,
                device_id=device_id,
                device_label=device_label,
                verify_key=self._verify_key,
            )
            redacted_device_certificate = device_certificate.evolve(
                device_label=None)

            device_certificate = device_certificate.dump_and_sign(
                author.signing_key)
            redacted_device_certificate = redacted_device_certificate.dump_and_sign(
                author.signing_key)

        except DataError as exc:
            raise InviteError(
                f"Cannot generate device certificate: {exc}") from exc

        rep = await self._cmds.device_create(
            device_certificate=device_certificate,
            redacted_device_certificate=redacted_device_certificate,
        )
        _check_rep(rep, step_name="device creation")

        try:
            payload = InviteDeviceConfirmation(
                device_id=device_id,
                device_label=device_label,
                human_handle=author.human_handle,
                profile=author.profile,
                private_key=author.private_key,
                user_manifest_id=author.user_manifest_id,
                user_manifest_key=author.user_manifest_key,
                root_verify_key=author.root_verify_key,
            ).dump_and_encrypt(key=self._shared_secret_key)
        except DataError as exc:
            raise InviteError(
                "Cannot generate InviteUserConfirmation payload") from exc

        rep = await self._cmds.invite_4_greeter_communicate(token=self.token,
                                                            payload=payload)
        _check_rep(rep, step_name="step 4 (confirmation exchange)")

        await self._cmds.invite_delete(token=self.token,
                                       reason=InvitationDeletedReason.FINISHED)
Example #15
0
 def new_device(self, user, author_user, author_device_rand):
     author = self.get_device(author_user, author_device_rand)
     device_id = self.next_device_id(user)
     note(f"new device: {device_id} (author: {author.device_id})")
     local_device = local_device_factory(device_id, org=coolorg)
     device = DeviceCertificateContent(
         author=author.device_id,
         timestamp=pendulum_now(),
         device_id=local_device.device_id,
         verify_key=local_device.verify_key,
     )
     self.devices_content[local_device.device_id] = device
     self.devices_certifs[local_device.device_id] = device.dump_and_sign(author.signing_key)
Example #16
0
async def _register_new_user(
    cmds: BackendAuthenticatedCmds,
    author: LocalDevice,
    device_label: Optional[str],
    human_handle: Optional[HumanHandle],
    profile: UserProfile,
) -> LocalDevice:
    new_device = generate_new_device(
        organization_addr=cmds.addr,
        device_label=device_label,
        human_handle=human_handle,
        profile=profile,
    )
    now = pendulum_now()

    user_certificate = UserCertificateContent(
        author=author.device_id,
        timestamp=now,
        user_id=new_device.device_id.user_id,
        human_handle=new_device.human_handle,
        public_key=new_device.public_key,
        profile=new_device.profile,
    )
    redacted_user_certificate = user_certificate.evolve(human_handle=None)

    device_certificate = DeviceCertificateContent(
        author=author.device_id,
        timestamp=now,
        device_id=new_device.device_id,
        device_label=new_device.device_label,
        verify_key=new_device.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)

    user_certificate = user_certificate.dump_and_sign(author.signing_key)
    redacted_user_certificate = redacted_user_certificate.dump_and_sign(
        author.signing_key)
    device_certificate = device_certificate.dump_and_sign(author.signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(
        author.signing_key)

    rep = await cmds.user_create(
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=redacted_user_certificate,
        redacted_device_certificate=redacted_device_certificate,
    )
    if rep["status"] != "ok":
        raise RuntimeError(f"Cannot create user: {rep}")

    return new_device
Example #17
0
async def test_user_create_invalid_certificate(backend,
                                               apiv1_backend_sock_factory,
                                               alice, bob, mallory):
    now = pendulum.now()
    good_user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=now,
        user_id=mallory.user_id,
        human_handle=None,
        public_key=mallory.public_key,
        profile=UserProfile.STANDARD,
    ).dump_and_sign(alice.signing_key)
    good_device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=mallory.device_id,
        device_label=None,
        verify_key=mallory.verify_key,
    ).dump_and_sign(alice.signing_key)
    bad_user_certificate = UserCertificateContent(
        author=bob.device_id,
        timestamp=now,
        user_id=mallory.user_id,
        human_handle=None,
        public_key=mallory.public_key,
        profile=UserProfile.STANDARD,
    ).dump_and_sign(bob.signing_key)
    bad_device_certificate = DeviceCertificateContent(
        author=bob.device_id,
        timestamp=now,
        device_id=mallory.device_id,
        device_label=None,
        verify_key=mallory.verify_key,
    ).dump_and_sign(bob.signing_key)

    async with apiv1_backend_sock_factory(backend, alice) as sock:
        for cu, cd in [
            (good_user_certificate, bad_device_certificate),
            (bad_user_certificate, good_device_certificate),
            (bad_user_certificate, bad_device_certificate),
        ]:
            rep = await user_create(sock,
                                    user_certificate=cu,
                                    device_certificate=cd)
            assert rep == {
                "status":
                "invalid_certification",
                "reason":
                "Invalid certification data (Signature was forged or corrupt).",
            }
Example #18
0
async def _create_new_device_for_self(
        original_device: LocalDevice,
        new_device_label: DeviceLabel) -> LocalDevice:
    """
    Raises:
        BackendConnectionError
    """
    new_device = LocalDevice(
        organization_addr=original_device.organization_addr,
        device_id=DeviceID(f"{original_device.user_id}@{DeviceName.new()}"),
        device_label=new_device_label,
        human_handle=original_device.human_handle,
        profile=original_device.profile,
        private_key=original_device.private_key,
        signing_key=SigningKey.generate(),
        user_manifest_id=original_device.user_manifest_id,
        user_manifest_key=original_device.user_manifest_key,
        local_symkey=SecretKey.generate(),
    )
    now = pendulum_now()

    device_certificate = DeviceCertificateContent(
        author=original_device.device_id,
        timestamp=now,
        device_id=new_device.device_id,
        device_label=new_device.device_label,
        verify_key=new_device.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)

    device_certificate = device_certificate.dump_and_sign(
        original_device.signing_key)
    redacted_device_certificate = redacted_device_certificate.dump_and_sign(
        original_device.signing_key)

    async with backend_authenticated_cmds_factory(
            addr=original_device.organization_addr,
            device_id=original_device.device_id,
            signing_key=original_device.signing_key,
    ) as cmds:
        rep = await cmds.device_create(
            device_certificate=device_certificate,
            redacted_device_certificate=redacted_device_certificate,
        )

    if rep["status"] != "ok":
        raise BackendConnectionError(f"Cannot create recovery device: {rep}")

    return new_device
Example #19
0
def validate_new_device_certificate(
    expected_author: DeviceID,
    author_verify_key: VerifyKey,
    device_certificate: bytes,
    redacted_device_certificate: bytes,
) -> Device:
    try:
        data = DeviceCertificateContent.verify_and_load(
            device_certificate,
            author_verify_key=author_verify_key,
            expected_author=expected_author)

        redacted_data = DeviceCertificateContent.verify_and_load(
            redacted_device_certificate,
            author_verify_key=author_verify_key,
            expected_author=expected_author,
        )

    except DataError as exc:
        raise CertificateValidationError(
            "invalid_certification", f"Invalid certification data ({exc}).")

    if not timestamps_in_the_ballpark(data.timestamp, pendulum.now()):
        raise CertificateValidationError(
            "invalid_certification", f"Invalid timestamp in certification.")

    if data.device_id.user_id != expected_author.user_id:
        raise CertificateValidationError(
            "bad_user_id", "Device must be handled by it own user.")

    if redacted_data.evolve(device_label=data.device_label) != data:
        raise CertificateValidationError(
            "invalid_data",
            "Redacted Device certificate differs from Device certificate.")
    if redacted_data.device_label:
        raise CertificateValidationError(
            "invalid_data",
            "Redacted Device certificate must not contain a device_label field."
        )

    return Device(
        device_id=data.device_id,
        device_label=data.device_label,
        device_certificate=device_certificate,
        redacted_device_certificate=redacted_device_certificate,
        device_certifier=data.author,
        created_on=data.timestamp,
    )
async def test_device_create_ok(backend, backend_sock_factory,
                                alice_backend_sock, alice, alice_nd):
    now = pendulum.now()
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=alice_nd.device_id,
        verify_key=alice_nd.verify_key,
    ).dump_and_sign(alice.signing_key)

    with backend.event_bus.listen() as spy:
        rep = await device_create(alice_backend_sock,
                                  device_certificate=device_certificate,
                                  encrypted_answer=b"<good>")
        assert rep == {"status": "ok"}

        # No guarantees this event occurs before the command's return
        await spy.wait_with_timeout(
            "device.created",
            {
                "organization_id": alice_nd.organization_id,
                "device_id": alice_nd.device_id,
                "device_certificate": device_certificate,
                "encrypted_answer": b"<good>",
            },
        )

    # Make sure the new device can connect now
    async with backend_sock_factory(backend, alice_nd) as sock:
        await sock.send(packb({"cmd": "ping", "ping": "Hello world !"}))
        raw_rep = await sock.recv()
        rep = ping_serializer.rep_loads(raw_rep)
        assert rep == {"status": "ok", "pong": "Hello world !"}
Example #21
0
    async def _alice_invite():
        rep = await alice_backend_cmds.user_invite(mallory.user_id)
        assert rep["status"] == "ok"
        claim = UserClaimContent.decrypt_and_load_for(
            rep["encrypted_claim"], recipient_privkey=alice.private_key
        )

        assert claim.token == token

        now = pendulum.now()
        user_certificate = UserCertificateContent(
            author=alice.device_id,
            timestamp=now,
            user_id=claim.device_id.user_id,
            public_key=claim.public_key,
            is_admin=False,
        ).dump_and_sign(alice.signing_key)
        device_certificate = DeviceCertificateContent(
            author=alice.device_id,
            timestamp=now,
            device_id=claim.device_id,
            verify_key=claim.verify_key,
        ).dump_and_sign(alice.signing_key)
        with trio.fail_after(1):
            rep = await alice_backend_cmds.user_create(user_certificate, device_certificate)
            assert rep["status"] == "ok"
Example #22
0
async def test_user_create_certificate_too_old(alice_backend_sock, alice, mallory):
    too_old = pendulum.datetime(2000, 1, 1)
    now = too_old.add(seconds=INVITATION_VALIDITY + 1)
    user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=too_old,
        user_id=mallory.user_id,
        human_handle=mallory.human_handle,
        public_key=mallory.public_key,
        profile=UserProfile.STANDARD,
    ).dump_and_sign(alice.signing_key)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=too_old,
        device_id=mallory.device_id,
        device_label=mallory.device_label,
        verify_key=mallory.verify_key,
    ).dump_and_sign(alice.signing_key)

    with freeze_time(now):
        rep = await user_create(
            alice_backend_sock,
            user_certificate=user_certificate,
            device_certificate=device_certificate,
            redacted_user_certificate=user_certificate,
            redacted_device_certificate=device_certificate,
        )
        assert rep == {
            "status": "invalid_certification",
            "reason": "Invalid timestamp in certificate.",
        }
Example #23
0
async def test_user_create_not_matching_certified_on(alice_backend_sock, alice, mallory):
    date1 = pendulum.datetime(2000, 1, 1)
    date2 = date1.add(seconds=1)
    user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=date1,
        user_id=mallory.user_id,
        human_handle=mallory.human_handle,
        public_key=mallory.public_key,
        profile=UserProfile.STANDARD,
    ).dump_and_sign(alice.signing_key)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=date2,
        device_id=mallory.device_id,
        device_label=mallory.device_label,
        verify_key=mallory.verify_key,
    ).dump_and_sign(alice.signing_key)
    with freeze_time(date1):
        rep = await user_create(
            alice_backend_sock,
            user_certificate=user_certificate,
            device_certificate=device_certificate,
            redacted_user_certificate=user_certificate,
            redacted_device_certificate=device_certificate,
        )
        assert rep == {
            "status": "invalid_data",
            "reason": "Device and User certificates must have the same timestamp.",
        }
Example #24
0
async def test_user_create_already_exists(alice_backend_sock, alice, bob):
    now = pendulum.now()
    user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=now,
        user_id=bob.user_id,
        human_handle=None,
        public_key=bob.public_key,
        profile=UserProfile.STANDARD,
    ).dump_and_sign(alice.signing_key)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=bob.device_id,
        device_label=None,
        verify_key=bob.verify_key,
    ).dump_and_sign(alice.signing_key)

    rep = await user_create(
        alice_backend_sock,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=user_certificate,
        redacted_device_certificate=device_certificate,
    )
    assert rep == {"status": "already_exists", "reason": f"User `{bob.user_id}` already exists"}
Example #25
0
async def test_user_create_not_matching_user_device(alice_backend_sock, alice, bob, mallory):
    now = pendulum.now()
    user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=now,
        user_id=mallory.user_id,
        human_handle=mallory.human_handle,
        public_key=mallory.public_key,
        profile=UserProfile.STANDARD,
    ).dump_and_sign(alice.signing_key)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=bob.device_id,
        device_label=mallory.device_label,
        verify_key=mallory.verify_key,
    ).dump_and_sign(alice.signing_key)

    rep = await user_create(
        alice_backend_sock,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=user_certificate,
        redacted_device_certificate=device_certificate,
    )
    assert rep == {
        "status": "invalid_data",
        "reason": "Device and User must have the same user ID.",
    }
Example #26
0
async def test_user_create_human_handle_with_revoked_previous_one(
        alice_backend_sock, alice, bob, backend_data_binder):
    # First revoke bob
    await backend_data_binder.bind_revocation(user_id=bob.user_id,
                                              certifier=alice)

    # Now recreate another user with bob's human handle
    now = pendulum.now()
    bob2_device_id = DeviceID("bob2@dev1")
    user_certificate = UserCertificateContent(
        author=alice.device_id,
        timestamp=now,
        user_id=bob2_device_id.user_id,
        public_key=bob.public_key,
        is_admin=False,
        human_handle=bob.human_handle,
    ).dump_and_sign(alice.signing_key)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=bob2_device_id,
        verify_key=bob.verify_key).dump_and_sign(alice.signing_key)

    rep = await user_create(alice_backend_sock,
                            user_certificate=user_certificate,
                            device_certificate=device_certificate)
    assert rep == {"status": "ok"}
Example #27
0
async def test_user_create_not_matching_certified_on(alice_backend_sock, alice,
                                                     mallory):
    date1 = pendulum.Pendulum(2000, 1, 1)
    date2 = date1.add(seconds=1)
    cu = UserCertificateContent(
        author=alice.device_id,
        timestamp=date1,
        user_id=mallory.user_id,
        public_key=mallory.public_key,
        is_admin=False,
    ).dump_and_sign(alice.signing_key)
    cd = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=date2,
        device_id=mallory.device_id,
        verify_key=mallory.verify_key,
    ).dump_and_sign(alice.signing_key)
    with freeze_time(date1):
        rep = await user_create(alice_backend_sock,
                                user_certificate=cu,
                                device_certificate=cd)
        assert rep == {
            "status":
            "invalid_data",
            "reason":
            "Device and User certifications must have the same timestamp.",
        }
Example #28
0
async def test_user_create_certify_too_old(alice_backend_sock, alice, mallory):
    too_old = pendulum.Pendulum(2000, 1, 1)
    now = too_old.add(seconds=INVITATION_VALIDITY + 1)
    cu = UserCertificateContent(
        author=alice.device_id,
        timestamp=too_old,
        user_id=mallory.user_id,
        public_key=mallory.public_key,
        is_admin=False,
    ).dump_and_sign(alice.signing_key)
    cd = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=too_old,
        device_id=mallory.device_id,
        verify_key=mallory.verify_key,
    ).dump_and_sign(alice.signing_key)

    with freeze_time(now):
        rep = await user_create(alice_backend_sock,
                                user_certificate=cu,
                                device_certificate=cd)
        assert rep == {
            "status": "invalid_certification",
            "reason": "Invalid timestamp in certification.",
        }
Example #29
0
async def test_user_create_author_not_admin(backend, bob_backend_sock, bob,
                                            mallory):
    # Unlike alice, bob is not admin
    now = pendulum.now()
    user_certificate = UserCertificateContent(
        author=bob.device_id,
        timestamp=now,
        user_id=mallory.user_id,
        public_key=mallory.public_key,
        is_admin=False,
    ).dump_and_sign(bob.signing_key)
    device_certificate = DeviceCertificateContent(
        author=bob.device_id,
        timestamp=now,
        device_id=mallory.device_id,
        verify_key=mallory.verify_key,
    ).dump_and_sign(bob.signing_key)

    rep = await user_create(bob_backend_sock,
                            user_certificate=user_certificate,
                            device_certificate=device_certificate)
    assert rep == {
        "status": "not_allowed",
        "reason": "User `bob` is not admin"
    }
Example #30
0
def local_device_to_backend_user(
    device: LocalDevice, certifier: Union[LocalDevice, OrganizationFullData]
) -> Tuple[BackendUser, BackendDevice]:
    if isinstance(certifier, OrganizationFullData):
        certifier_id = None
        certifier_signing_key = certifier.root_signing_key
    else:
        certifier_id = certifier.device_id
        certifier_signing_key = certifier.signing_key

    now = pendulum.now()
    user_certificate = UserCertificateContent(
        author=certifier_id,
        timestamp=now,
        user_id=device.user_id,
        public_key=device.public_key,
        is_admin=device.is_admin,
        human_handle=device.human_handle,
    ).dump_and_sign(certifier_signing_key)
    device_certificate = DeviceCertificateContent(
        author=certifier_id,
        timestamp=now,
        device_id=device.device_id,
        verify_key=device.verify_key).dump_and_sign(certifier_signing_key)
    return new_backend_user_factory(
        device_id=device.device_id,
        human_handle=device.human_handle,
        is_admin=device.is_admin,
        certifier=certifier_id,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
    )