Пример #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,
                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
Пример #2
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
Пример #3
0
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,
        human_handle=mallory.human_handle,
        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=mallory.device_label,
        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)

    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.",
        }
Пример #4
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,
        profile=device.profile,
        human_handle=device.human_handle,
    )
    device_certificate = DeviceCertificateContent(
        author=certifier_id,
        timestamp=now,
        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=now,
    )

    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=now,
    )

    return user, first_device
Пример #5
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)
Пример #6
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
Пример #7
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,
         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(author.signing_key)
Пример #8
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.",
        }
Пример #9
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.",
        }
Пример #10
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"
    }
Пример #11
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.",
    }
Пример #12
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,
        human_handle=bob.human_handle,
        public_key=bob.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=bob2_device_id,
        device_label=bob.
        device_label,  # Device label doesn't have to be unique
        verify_key=bob.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)

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

    rep = await user_create(
        alice_backend_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"}
Пример #13
0
async def test_user_create_human_handle_already_exists(alice_backend_sock,
                                                       alice, bob):
    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,
        human_handle=bob.human_handle,
        public_key=bob.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=bob2_device_id,
        device_label="dev2",
        verify_key=bob.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)

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

    rep = await user_create(
        alice_backend_sock,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=redacted_user_certificate,
        redacted_device_certificate=redacted_device_certificate,
    )
    assert rep == {
        "status":
        "already_exists",
        "reason":
        f"Human handle `{bob.human_handle}` already corresponds to a non-revoked user",
    }
Пример #14
0
async def test_device_create_bad_redacted_device_certificate(
        alice_backend_sock, alice, alice_nd):
    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,
    )
    good_redacted_device_certificate = device_certificate.evolve(
        device_label=None)
    device_certificate = device_certificate.dump_and_sign(alice.signing_key)
    for bad_redacted_device_certificate in (
            good_redacted_device_certificate.evolve(timestamp=now.add(
                seconds=1)),
            good_redacted_device_certificate.evolve(device_id=alice.device_id),
            good_redacted_device_certificate.evolve(
                verify_key=alice.verify_key),
    ):
        rep = await device_create(
            alice_backend_sock,
            device_certificate=device_certificate,
            redacted_device_certificate=bad_redacted_device_certificate.
            dump_and_sign(alice.signing_key),
        )
        assert rep == {
            "status":
            "invalid_data",
            "reason":
            "Redacted Device certificate differs from Device certificate.",
        }

    # Finally just make sure good was really good
    rep = await device_create(
        alice_backend_sock,
        device_certificate=device_certificate,
        redacted_device_certificate=good_redacted_device_certificate.
        dump_and_sign(alice.signing_key),
    )
    assert rep == {"status": "ok"}
Пример #15
0
async def test_device_create_invalid_certified(alice_backend_sock, alice, bob,
                                               alice_nd):
    now = pendulum.now()
    good_device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=alice_nd.device_id,
        device_label=None,  # Can be used as regular and redacted certificate
        verify_key=alice_nd.verify_key,
    ).dump_and_sign(alice.signing_key)
    bad_device_certificate = DeviceCertificateContent(
        author=bob.device_id,
        timestamp=now,
        device_id=alice_nd.device_id,
        device_label=None,  # Can be used as regular and redacted certificate
        verify_key=alice_nd.verify_key,
    ).dump_and_sign(bob.signing_key)

    rep = await device_create(
        alice_backend_sock,
        device_certificate=bad_device_certificate,
        redacted_device_certificate=good_device_certificate,
    )
    assert rep == {
        "status": "invalid_certification",
        "reason":
        "Invalid certification data (Signature was forged or corrupt).",
    }

    # Same for the redacted part

    rep = await device_create(
        alice_backend_sock,
        device_certificate=good_device_certificate,
        redacted_device_certificate=bad_device_certificate,
    )
    assert rep == {
        "status": "invalid_certification",
        "reason":
        "Invalid certification data (Signature was forged or corrupt).",
    }
Пример #16
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)

    rep = await device_create(
        alice_backend_sock,
        device_certificate=device_certificate,
        redacted_device_certificate=redacted_device_certificate,
    )
    assert rep == {"status": "ok"}

    # 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,
    )
Пример #17
0
async def _register_new_device(cmds: BackendAuthenticatedCmds,
                               author: LocalDevice,
                               device_label: Optional[str]):
    new_device = LocalDevice(
        organization_addr=author.organization_addr,
        device_id=DeviceID(f"{author.user_id}@{DeviceName.new()}"),
        device_label=device_label,
        human_handle=author.human_handle,
        profile=author.profile,
        private_key=author.private_key,
        signing_key=SigningKey.generate(),
        user_manifest_id=author.user_manifest_id,
        user_manifest_key=author.user_manifest_key,
        local_symkey=author.local_symkey,
    )
    now = pendulum_now()

    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)

    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.device_create(
        device_certificate=device_certificate,
        redacted_device_certificate=redacted_device_certificate,
    )

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

    return new_device
Пример #18
0
def test_build_device_certificate(alice, bob, mallory):
    now = pendulum_now()
    certif = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=bob.device_id,
        device_label=bob.device_label,
        verify_key=bob.verify_key,
    ).dump_and_sign(alice.signing_key)
    assert isinstance(certif, bytes)

    unsecure = DeviceCertificateContent.unsecure_load(certif)
    assert isinstance(unsecure, DeviceCertificateContent)
    assert unsecure.device_id == bob.device_id
    assert unsecure.verify_key == bob.verify_key
    assert unsecure.timestamp == now
    assert unsecure.author == alice.device_id

    verified = DeviceCertificateContent.verify_and_load(
        certif, author_verify_key=alice.verify_key, expected_author=alice.device_id
    )
    assert verified == unsecure

    with pytest.raises(DataError) as exc:
        DeviceCertificateContent.verify_and_load(
            certif, author_verify_key=alice.verify_key, expected_author=mallory.device_id
        )
    assert str(exc.value) == "Invalid author: expected `mallory@dev1`, got `alice@dev1`"

    with pytest.raises(DataError) as exc:
        DeviceCertificateContent.verify_and_load(
            certif, author_verify_key=mallory.verify_key, expected_author=alice.device_id
        )
    assert str(exc.value) == "Signature was forged or corrupt"

    with pytest.raises(DataError) as exc:
        DeviceCertificateContent.verify_and_load(
            certif,
            author_verify_key=alice.verify_key,
            expected_author=alice.device_id,
            expected_device=mallory.device_id,
        )
    assert str(exc.value) == "Invalid device ID: expected `mallory@dev1`, got `bob@dev1`"
Пример #19
0
async def test_device_create_already_exists(alice_backend_sock, alice, alice2):
    now = pendulum.now()
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=alice2.device_id,
        device_label=None,
        verify_key=alice2.verify_key,
    ).dump_and_sign(alice.signing_key)

    rep = await device_create(
        alice_backend_sock,
        device_certificate=device_certificate,
        redacted_device_certificate=device_certificate,
    )
    assert rep == {
        "status": "already_exists",
        "reason": f"Device `{alice2.device_id}` already exists",
    }
Пример #20
0
async def test_device_create_not_own_user(bob_backend_sock, bob, alice_nd):
    now = pendulum.now()
    device_certificate = DeviceCertificateContent(
        author=bob.device_id,
        timestamp=now,
        device_id=alice_nd.device_id,
        device_label=None,
        verify_key=alice_nd.verify_key,
    ).dump_and_sign(bob.signing_key)

    rep = await device_create(
        bob_backend_sock,
        device_certificate=device_certificate,
        redacted_device_certificate=device_certificate,
    )
    assert rep == {
        "status": "bad_user_id",
        "reason": "Device must be handled by it own user."
    }
Пример #21
0
async def test_device_create_certify_too_old(alice_backend_sock, alice,
                                             alice_nd):
    now = pendulum.datetime(2000, 1, 1)
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=alice_nd.device_id,
        device_label=None,
        verify_key=alice_nd.verify_key,
    ).dump_and_sign(alice.signing_key)

    with freeze_time(now.add(seconds=INVITATION_VALIDITY + 1)):
        rep = await device_create(
            alice_backend_sock,
            device_certificate=device_certificate,
            redacted_device_certificate=device_certificate,
        )
        assert rep == {
            "status": "invalid_certification",
            "reason": "Invalid timestamp in certification.",
        }
Пример #22
0
async def test_redacted_certificates_cannot_contain_sensitive_data(
        alice_backend_sock, alice, alice_nd):
    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,
    ).dump_and_sign(alice.signing_key)

    with freeze_time(now):
        rep = await device_create(
            alice_backend_sock,
            device_certificate=device_certificate,
            redacted_device_certificate=device_certificate,
        )
        assert rep == {
            "status":
            "invalid_data",
            "reason":
            "Redacted Device certificate must not contain a device_label field.",
        }
Пример #23
0
async def test_user_create_invalid_certificate(alice_backend_sock, alice, bob,
                                               mallory):
    now = pendulum.now()
    good_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)
    good_device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=mallory.device_id,
        device_label=mallory.device_label,
        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=mallory.human_handle,
        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=mallory.device_label,
        verify_key=mallory.verify_key,
    ).dump_and_sign(bob.signing_key)

    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(
            alice_backend_sock,
            user_certificate=cu,
            device_certificate=cd,
            redacted_user_certificate=good_user_certificate,
            redacted_device_certificate=good_device_certificate,
        )
        assert rep == {
            "status":
            "invalid_certification",
            "reason":
            "Invalid certification data (Signature was forged or corrupt).",
        }

    # Same thing for the redacted part
    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(
            alice_backend_sock,
            user_certificate=good_user_certificate,
            device_certificate=good_device_certificate,
            redacted_user_certificate=cu,
            redacted_device_certificate=cd,
        )
        assert rep == {
            "status":
            "invalid_certification",
            "reason":
            "Invalid certification data (Signature was forged or corrupt).",
        }
Пример #24
0
    async def _api_user_create(self, client_ctx, msg):
        try:
            d_data = DeviceCertificateContent.verify_and_load(
                msg["device_certificate"],
                author_verify_key=client_ctx.verify_key,
                expected_author=client_ctx.device_id,
            )
            u_data = UserCertificateContent.verify_and_load(
                msg["user_certificate"],
                author_verify_key=client_ctx.verify_key,
                expected_author=client_ctx.device_id,
            )
            ru_data = UserCertificateContent.verify_and_load(
                msg["redacted_user_certificate"],
                author_verify_key=client_ctx.verify_key,
                expected_author=client_ctx.device_id,
            )
            rd_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 u_data.timestamp != d_data.timestamp:
            return {
                "status":
                "invalid_data",
                "reason":
                "Device and User certificates must have the same timestamp.",
            }

        now = pendulum.now()
        if not timestamps_in_the_ballpark(u_data.timestamp, now):
            return {
                "status": "invalid_certification",
                "reason": "Invalid timestamp in certificate.",
            }

        if u_data.user_id != d_data.device_id.user_id:
            return {
                "status": "invalid_data",
                "reason": "Device and User must have the same user ID.",
            }

        if ru_data.evolve(human_handle=u_data.human_handle) != u_data:
            return {
                "status":
                "invalid_data",
                "reason":
                "Redacted User certificate differs from User certificate.",
            }
        if ru_data.human_handle:
            return {
                "status":
                "invalid_data",
                "reason":
                "Redacted User certificate must not contain a human_handle field.",
            }

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

        try:
            user = User(
                user_id=u_data.user_id,
                human_handle=u_data.human_handle,
                profile=u_data.profile,
                user_certificate=msg["user_certificate"],
                redacted_user_certificate=msg["redacted_user_certificate"]
                or msg["user_certificate"],
                user_certifier=u_data.author,
                created_on=u_data.timestamp,
            )
            first_device = Device(
                device_id=d_data.device_id,
                device_label=d_data.device_label,
                device_certificate=msg["device_certificate"],
                redacted_device_certificate=msg["redacted_device_certificate"]
                or msg["device_certificate"],
                device_certifier=d_data.author,
                created_on=d_data.timestamp,
            )
            await self.create_user(client_ctx.organization_id, user,
                                   first_device)

        except UserAlreadyExistsError as exc:
            return {"status": "already_exists", "reason": str(exc)}

        return {"status": "ok"}
Пример #25
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": "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"})
Пример #26
0
 def verify_key(self) -> VerifyKey:
     return DeviceCertificateContent.unsecure_load(
         self.device_certificate).verify_key
Пример #27
0
    async def api_organization_bootstrap(self, client_ctx, msg):
        msg = apiv1_organization_bootstrap_serializer.req_load(msg)
        bootstrap_token = msg["bootstrap_token"]
        root_verify_key = msg["root_verify_key"]

        try:
            u_data = UserCertificateContent.verify_and_load(
                msg["user_certificate"],
                author_verify_key=root_verify_key,
                expected_author=None)
            d_data = DeviceCertificateContent.verify_and_load(
                msg["device_certificate"],
                author_verify_key=root_verify_key,
                expected_author=None)

            ru_data = rd_data = None
            if "redacted_user_certificate" in msg:
                ru_data = UserCertificateContent.verify_and_load(
                    msg["redacted_user_certificate"],
                    author_verify_key=root_verify_key,
                    expected_author=None,
                )
            if "redacted_device_certificate" in msg:
                rd_data = DeviceCertificateContent.verify_and_load(
                    msg["redacted_device_certificate"],
                    author_verify_key=root_verify_key,
                    expected_author=None,
                )

        except DataError as exc:
            return {
                "status": "invalid_certification",
                "reason": f"Invalid certification data ({exc}).",
            }
        if u_data.profile != UserProfile.ADMIN:
            return {
                "status": "invalid_data",
                "reason": "Bootstrapping user must have admin profile.",
            }

        if u_data.timestamp != d_data.timestamp:
            return {
                "status":
                "invalid_data",
                "reason":
                "Device and user certificates must have the same timestamp.",
            }

        if u_data.user_id != d_data.device_id.user_id:
            return {
                "status": "invalid_data",
                "reason": "Device and user must have the same user ID.",
            }

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

        if ru_data:
            if ru_data.evolve(human_handle=u_data.human_handle) != u_data:
                return {
                    "status":
                    "invalid_data",
                    "reason":
                    "Redacted User certificate differs from User certificate.",
                }
            if ru_data.human_handle:
                return {
                    "status":
                    "invalid_data",
                    "reason":
                    "Redacted User certificate must not contain a human_handle field.",
                }

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

        if (rd_data and not ru_data) or (ru_data and not rd_data):
            return {
                "status":
                "invalid_data",
                "reason":
                "Redacted user&device certificate muste be provided together",
            }

        user = User(
            user_id=u_data.user_id,
            human_handle=u_data.human_handle,
            profile=u_data.profile,
            user_certificate=msg["user_certificate"],
            redacted_user_certificate=msg.get("redacted_user_certificate",
                                              msg["user_certificate"]),
            user_certifier=u_data.author,
            created_on=u_data.timestamp,
        )
        first_device = Device(
            device_id=d_data.device_id,
            device_label=d_data.device_label,
            device_certificate=msg["device_certificate"],
            redacted_device_certificate=msg.get("redacted_device_certificate",
                                                msg["device_certificate"]),
            device_certifier=d_data.author,
            created_on=d_data.timestamp,
        )
        try:
            await self.bootstrap(client_ctx.organization_id, user,
                                 first_device, bootstrap_token,
                                 root_verify_key)

        except OrganizationAlreadyBootstrappedError:
            return {"status": "already_bootstrapped"}

        except (OrganizationNotFoundError,
                OrganizationInvalidBootstrapTokenError):
            return {"status": "not_found"}

        # Note: we let OrganizationFirstUserCreationError bobbles up given
        # it should not occurs under normal circumstances

        # Finally notify webhook
        await self.webhooks.on_organization_bootstrap(
            organization_id=client_ctx.organization_id,
            device_id=first_device.device_id,
            device_label=first_device.device_label,
            human_email=user.human_handle.email if user.human_handle else None,
            human_label=user.human_handle.label if user.human_handle else None,
        )

        return apiv1_organization_bootstrap_serializer.rep_dump(
            {"status": "ok"})
Пример #28
0
async def test_user_create_bad_redacted_user_certificate(
        alice_backend_sock, alice, mallory):
    now = pendulum.now()
    device_certificate = DeviceCertificateContent(
        author=alice.device_id,
        timestamp=now,
        device_id=mallory.device_id,
        device_label=None,  # Can be used as regular and redacted certificate
        verify_key=mallory.verify_key,
    ).dump_and_sign(alice.signing_key)
    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,
    )
    good_redacted_user_certificate = user_certificate.evolve(human_handle=None)
    user_certificate = user_certificate.dump_and_sign(alice.signing_key)
    for bad_redacted_user_certificate in (
            good_redacted_user_certificate.evolve(timestamp=now.add(
                seconds=1)),
            good_redacted_user_certificate.evolve(user_id=alice.user_id),
            good_redacted_user_certificate.evolve(public_key=alice.public_key),
            good_redacted_user_certificate.evolve(
                profile=UserProfile.OUTSIDER),
    ):
        rep = await user_create(
            alice_backend_sock,
            user_certificate=user_certificate,
            device_certificate=device_certificate,
            redacted_user_certificate=bad_redacted_user_certificate.
            dump_and_sign(alice.signing_key),
            redacted_device_certificate=device_certificate,
        )
        assert rep == {
            "status": "invalid_data",
            "reason":
            "Redacted User certificate differs from User certificate.",
        }

    # Missing redacted certificate is not allowed as well
    rep = await user_create(
        alice_backend_sock,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=None,
        redacted_device_certificate=device_certificate,
    )
    assert rep == {
        "status": "bad_message",
        "reason": "Invalid message.",
        "errors": {
            "redacted_user_certificate": ["Missing data for required field."]
        },
    }

    # Finally just make sure good was really good
    rep = await user_create(
        alice_backend_sock,
        user_certificate=user_certificate,
        device_certificate=device_certificate,
        redacted_user_certificate=good_redacted_user_certificate.dump_and_sign(
            alice.signing_key),
        redacted_device_certificate=device_certificate,
    )
    assert rep == {"status": "ok"}
Пример #29
0
    def load_trustchain(
        self,
        users: Sequence[bytes] = (),
        revoked_users: Sequence[bytes] = (),
        devices: Sequence[bytes] = (),
        now: DateTime = None,
    ) -> Tuple[List[UserCertificateContent],
               List[RevokedUserCertificateContent],
               List[DeviceCertificateContent], ]:
        now = now or pendulum_now()

        users_states = {}
        devices_states = {}
        revoked_users_states = {}

        # Deserialize the certificates and filter the ones we already have in cache
        try:
            for certif in devices:
                unverified_device = DeviceCertificateContent.unsecure_load(
                    certif)
                verified_device = self.get_device(unverified_device.device_id,
                                                  now)
                if verified_device:
                    devices_states[verified_device.device_id] = CertifState(
                        certif, verified_device, True)
                else:
                    devices_states[unverified_device.device_id] = CertifState(
                        certif, unverified_device, False)

            for certif in users:
                unverified_user = UserCertificateContent.unsecure_load(certif)
                verified_user = self.get_user(unverified_user.user_id, now)
                if verified_user:
                    users_states[verified_user.user_id] = CertifState(
                        certif, verified_user, True)
                else:
                    users_states[unverified_user.user_id] = CertifState(
                        certif, unverified_user, False)

            for certif in revoked_users:
                unverified_revoked_user = RevokedUserCertificateContent.unsecure_load(
                    certif)
                verified_revoked_user = self.get_revoked_user(
                    unverified_revoked_user.user_id, now)
                if verified_revoked_user:
                    revoked_users_states[
                        verified_revoked_user.user_id] = CertifState(
                            certif, verified_revoked_user, True)
                else:
                    revoked_users_states[
                        unverified_revoked_user.user_id] = CertifState(
                            certif, unverified_revoked_user, False)

        except DataError as exc:
            raise TrustchainError(f"Invalid certificate: {exc}") from exc

        def _get_eventually_verified_user(user_id):
            try:
                return users_states[user_id].content
            except KeyError:
                return None

        def _get_eventually_verified_revoked_user(user_id):
            try:
                return revoked_users_states[user_id].content
            except KeyError:
                return None

        def _verify_created_by_root(certif, certif_cls, sign_chain):
            try:
                return certif_cls.verify_and_load(
                    certif,
                    author_verify_key=self.root_verify_key,
                    expected_author=None)

            except DataError as exc:
                path = _build_signature_path(*sign_chain, "<Root Key>")
                raise TrustchainError(
                    f"{path}: Invalid certificate: {exc}") from exc

        def _verify_created_by_device(certif, certif_cls, author_id,
                                      sign_chain):
            author_device = _recursive_verify_device(author_id, sign_chain)
            try:
                verified = certif_cls.verify_and_load(
                    certif,
                    author_verify_key=author_device.verify_key,
                    expected_author=author_device.device_id,
                )

            except DataError as exc:
                path = _build_signature_path(*sign_chain, author_id)
                raise TrustchainError(
                    f"{path}: Invalid certificate: {exc}") from exc

            # Author is either admin or signing one of it own devices
            verified_user_id = (verified.device_id.user_id if isinstance(
                verified, DeviceCertificateContent) else verified.user_id)
            if author_device.device_id.user_id != verified_user_id:
                author_user = _get_eventually_verified_user(author_id.user_id)
                if not author_user:
                    path = _build_signature_path(*sign_chain, author_id)
                    raise TrustchainError(
                        f"{path}: Missing user certificate for {author_id.user_id}"
                    )
                elif author_user.profile != UserProfile.ADMIN:
                    path = _build_signature_path(*sign_chain, author_id)
                    raise TrustchainError(
                        f"{path}:  Invalid signature given {author_user.user_id} is not admin"
                    )
            # Also make sure author wasn't revoked at creation time
            author_revoked_user = _get_eventually_verified_revoked_user(
                author_id.user_id)
            if author_revoked_user and verified.timestamp > author_revoked_user.timestamp:
                path = _build_signature_path(*sign_chain, author_id)
                raise TrustchainError(
                    f"{path}: Signature ({verified.timestamp}) is posterior "
                    f"to user revocation ({author_revoked_user.timestamp})")

            return verified

        def _recursive_verify_device(device_id, signed_children=()):
            if device_id in signed_children:
                path = _build_signature_path(*signed_children, device_id)
                raise TrustchainError(
                    f"{path}: Invalid signature loop detected")

            try:
                state = devices_states[device_id]
            except KeyError:
                path = _build_signature_path(*signed_children, device_id)
                raise TrustchainError(
                    f"{path}: Missing device certificate for {device_id}")

            author = state.content.author
            if author is None:
                verified_device = _verify_created_by_root(
                    state.certif,
                    DeviceCertificateContent,
                    sign_chain=(*signed_children, device_id))
            else:
                verified_device = _verify_created_by_device(
                    state.certif,
                    DeviceCertificateContent,
                    author,
                    sign_chain=(*signed_children, device_id),
                )
            return verified_device

        def _verify_user(unverified_content, certif):
            author = unverified_content.author
            user_id = unverified_content.user_id
            if author is None:
                verified_user = _verify_created_by_root(
                    certif,
                    UserCertificateContent,
                    sign_chain=(f"{user_id}'s creation", ))
            elif author.user_id == user_id:
                raise TrustchainError(
                    f"{user_id}: Invalid self-signed user certificate")
            else:
                verified_user = _verify_created_by_device(
                    certif,
                    UserCertificateContent,
                    author,
                    sign_chain=(f"{user_id}'s creation", ))
            return verified_user

        def _verify_revoked_user(unverified_content, certif):
            author = unverified_content.author
            user_id = unverified_content.user_id
            if author is None:
                verified_revoked_user = _verify_created_by_root(
                    certif,
                    RevokedUserCertificateContent,
                    sign_chain=(f"{user_id}'s revocation", ))
            elif author.user_id == user_id:
                raise TrustchainError(
                    f"{user_id}: Invalid self-signed user revocation certificate"
                )
            else:
                verified_revoked_user = _verify_created_by_device(
                    certif,
                    RevokedUserCertificateContent,
                    author,
                    sign_chain=(f"{user_id}'s revocation", ),
                )
            return verified_revoked_user

        # Verified what need to be and populate the cache with them

        for certif_state in devices_states.values():
            if not certif_state.verified:
                certif_state.content = _recursive_verify_device(
                    certif_state.content.device_id)
        for certif_state in users_states.values():
            if not certif_state.verified:
                certif_state.content = _verify_user(certif_state.content,
                                                    certif_state.certif)
        for certif_state in revoked_users_states.values():
            if not certif_state.verified:
                certif_state.content = _verify_revoked_user(
                    certif_state.content, certif_state.certif)

        # Finally populate the cache
        for certif_state in devices_states.values():
            if not certif_state.verified:
                self._devices_cache[certif_state.content.device_id] = (
                    now, certif_state.content)
        for certif_state in users_states.values():
            if not certif_state.verified:
                self._users_cache[certif_state.content.user_id] = (
                    now, certif_state.content)
        for certif_state in revoked_users_states.values():
            if not certif_state.verified:
                self._revoked_users_cache[certif_state.content.user_id] = (
                    now,
                    certif_state.content,
                )

        return (
            [state.content for state in users_states.values()],
            [state.content for state in revoked_users_states.values()],
            [state.content for state in devices_states.values()],
        )
Пример #30
0
async def test_user_create_ok(backend, backend_sock_factory,
                              alice_backend_sock, alice, mallory, profile,
                              with_labels):
    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=profile,
    )
    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=mallory.device_label,
        verify_key=mallory.verify_key,
    )
    redacted_device_certificate = device_certificate.evolve(device_label=None)
    if not with_labels:
        user_certificate = redacted_user_certificate
        device_certificate = redacted_device_certificate

    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)

    rep = await user_create(
        alice_backend_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"}

    # Make sure mallory can connect now
    async with backend_sock_factory(backend, mallory) as sock:
        rep = await user_get(sock, user_id=mallory.user_id)
        assert rep["status"] == "ok"

    # Check the resulting data in the backend
    backend_user, backend_device = await backend.user.get_user_with_device(
        mallory.organization_id, mallory.device_id)
    assert backend_user == User(
        user_id=mallory.user_id,
        human_handle=mallory.human_handle if with_labels else None,
        profile=profile,
        user_certificate=user_certificate,
        redacted_user_certificate=redacted_user_certificate,
        user_certifier=alice.device_id,
        created_on=now,
    )
    assert backend_device == Device(
        device_id=mallory.device_id,
        device_label=mallory.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,
    )