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
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" }
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.", }
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.", }
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.", }
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
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.", }
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).", }
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
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)
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
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)
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", }
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"}
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." }
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`"
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", }
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"}
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, )
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.", }
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
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.", }
def _trustchain_data_factory(todo_devices, todo_users): data = TrustchainData(coolorg.organization_id, coolorg.root_verify_key) def _get_certifier_id_and_key(certifier): if not certifier: # Certified by root certifier_id = None certifier_key = coolorg.root_signing_key else: certifier_ld = data.get_local_device(certifier) if not certifier_ld: raise RuntimeError(f"Missing `{certifier}` to sign creation of `{todo_device}`") certifier_id = certifier_ld.device_id certifier_key = certifier_ld.signing_key return certifier_id, certifier_key # First create all the devices for todo_device in todo_devices: local_device = local_device_factory(todo_device["id"], org=coolorg) data.add_local_device(local_device) # Generate device certificate certifier_id, certifier_key = _get_certifier_id_and_key(todo_device.get("certifier")) created_on = todo_device.get("created_on", now) device_certificate = DeviceCertificateContent( author=certifier_id, timestamp=created_on, device_id=local_device.device_id, device_label=local_device.device_label, verify_key=local_device.verify_key, ) data.add_device_certif( device_certificate, device_certificate.dump_and_sign(certifier_key) ) # Now deal with the users for todo_user in todo_users: local_user = next( (u for u in data.local_devices.values() if str(u.user_id) == todo_user["id"]), None ) if not local_user: raise RuntimeError(f"Missing device for user `{todo_user['id']}`") # Generate user certificate certifier_id, certifier_key = _get_certifier_id_and_key(todo_user.get("certifier")) created_on = todo_user.get("created_on", now) user_certif = UserCertificateContent( author=certifier_id, timestamp=created_on, user_id=local_user.user_id, human_handle=local_device.human_handle, public_key=local_user.public_key, profile=todo_user.get("profile", UserProfile.STANDARD), ) data.add_user_certif(user_certif, user_certif.dump_and_sign(certifier_key)) # Generate user revocation certificate if needed revoker = todo_user.get("revoker", None) if revoker: revoked_on = todo_user.get("revoked_on", now) revoker_ld = data.get_local_device(revoker) if not revoker_ld: raise RuntimeError( f"Missing `{revoker}` to sign revocation of `{todo_user['id']}`" ) revoked_user_certificate = RevokedUserCertificateContent( author=revoker_ld.device_id, timestamp=revoked_on, user_id=local_user.user_id ) data.add_revoked_user_certif( revoked_user_certificate, revoked_user_certificate.dump_and_sign(revoker_ld.signing_key), ) return data
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).", }
async def test_organization_bootstrap_bad_data( backend_data_binder, apiv1_backend_sock_factory, organization_factory, local_device_factory, backend, coolorg, ): neworg = organization_factory("NewOrg") newalice = local_device_factory("alice@dev1", neworg) await backend_data_binder.bind_organization(neworg) bad_organization_id = coolorg.organization_id good_organization_id = neworg.organization_id root_signing_key = neworg.root_signing_key bad_root_signing_key = coolorg.root_signing_key good_bootstrap_token = neworg.bootstrap_token bad_bootstrap_token = coolorg.bootstrap_token good_rvk = neworg.root_verify_key bad_rvk = coolorg.root_verify_key good_device_id = newalice.device_id good_user_id = newalice.user_id bad_user_id = UserID("dummy") public_key = newalice.public_key verify_key = newalice.verify_key now = pendulum.now() bad_now = now.subtract(seconds=1) good_cu = UserCertificateContent( author=None, timestamp=now, user_id=good_user_id, public_key=public_key, profile=UserProfile.ADMIN, human_handle=newalice.human_handle, ) good_redacted_cu = good_cu.evolve(human_handle=None) good_cd = DeviceCertificateContent( author=None, timestamp=now, device_id=good_device_id, device_label=newalice.device_label, verify_key=verify_key, ) good_redacted_cd = good_cd.evolve(device_label=None) bad_now_cu = good_cu.evolve(timestamp=bad_now) bad_now_cd = good_cd.evolve(timestamp=bad_now) bad_now_redacted_cu = good_redacted_cu.evolve(timestamp=bad_now) bad_now_redacted_cd = good_redacted_cd.evolve(timestamp=bad_now) bad_id_cu = good_cu.evolve(user_id=bad_user_id) bad_not_admin_cu = good_cu.evolve(profile=UserProfile.STANDARD) bad_key_cu = good_cu.dump_and_sign(bad_root_signing_key) bad_key_cd = good_cd.dump_and_sign(bad_root_signing_key) good_cu = good_cu.dump_and_sign(root_signing_key) good_redacted_cu = good_redacted_cu.dump_and_sign(root_signing_key) good_cd = good_cd.dump_and_sign(root_signing_key) good_redacted_cd = good_redacted_cd.dump_and_sign(root_signing_key) bad_now_cu = bad_now_cu.dump_and_sign(root_signing_key) bad_now_cd = bad_now_cd.dump_and_sign(root_signing_key) bad_now_redacted_cu = bad_now_redacted_cu.dump_and_sign(root_signing_key) bad_now_redacted_cd = bad_now_redacted_cd.dump_and_sign(root_signing_key) bad_id_cu = bad_id_cu.dump_and_sign(root_signing_key) bad_not_admin_cu = bad_not_admin_cu.dump_and_sign(root_signing_key) for i, (status, organization_id, *params) in enumerate([ ("not_found", good_organization_id, bad_bootstrap_token, good_cu, good_cd, good_rvk), ( "already_bootstrapped", bad_organization_id, good_bootstrap_token, good_cu, good_cd, good_rvk, ), ( "invalid_certification", good_organization_id, good_bootstrap_token, good_cu, good_cd, bad_rvk, ), ( "invalid_data", good_organization_id, good_bootstrap_token, bad_now_cu, good_cd, good_rvk, ), ( "invalid_data", good_organization_id, good_bootstrap_token, bad_id_cu, good_cd, good_rvk, ), ( "invalid_certification", good_organization_id, good_bootstrap_token, bad_key_cu, good_cd, good_rvk, ), ( "invalid_data", good_organization_id, good_bootstrap_token, good_cu, bad_now_cd, good_rvk, ), ( "invalid_certification", good_organization_id, good_bootstrap_token, good_cu, bad_key_cd, good_rvk, ), ( "invalid_data", good_organization_id, good_bootstrap_token, bad_not_admin_cu, good_cd, good_rvk, ), # Tests with redacted certificates ( "invalid_data", good_organization_id, good_bootstrap_token, good_cu, good_cd, good_rvk, good_cu, # Not redacted ! good_redacted_cd, ), ( "invalid_data", good_organization_id, good_bootstrap_token, good_cu, good_cd, good_rvk, good_redacted_cu, good_cd, # Not redacted ! ), ( "bad_message", good_organization_id, good_bootstrap_token, good_cu, good_cd, good_rvk, None, # None not allowed good_redacted_cd, ), ( "bad_message", good_organization_id, good_bootstrap_token, good_cu, good_cd, good_rvk, good_redacted_cu, None, # None not allowed ), ( "invalid_data", good_organization_id, good_bootstrap_token, good_cu, good_cd, good_rvk, bad_now_redacted_cu, good_redacted_cd, ), ( "invalid_data", good_organization_id, good_bootstrap_token, good_cu, good_cd, good_rvk, good_redacted_cu, bad_now_redacted_cd, ), ( "invalid_data", good_organization_id, good_bootstrap_token, good_cu, good_cd, good_rvk, good_redacted_cu, _missing, # Must proved redacted_device if redacted user is present ), ( "invalid_data", good_organization_id, good_bootstrap_token, good_cu, good_cd, good_rvk, _missing, # Must proved redacted_device if redacted user is present good_redacted_cd, ), ]): print(f"sub test {i}") async with apiv1_backend_sock_factory(backend, organization_id) as sock: rep = await organization_bootstrap(sock, *params) assert rep["status"] == status # Finally cheap test to make sure our "good" data were really good async with apiv1_backend_sock_factory(backend, good_organization_id) as sock: rep = await organization_bootstrap( sock, good_bootstrap_token, good_cu, good_cd, good_rvk, good_redacted_cu, good_redacted_cd, ) assert rep["status"] == "ok"
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"}
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, )
async def do_create_new_user( self, author: LocalDevice, device_label: Optional[str], human_handle: Optional[HumanHandle], profile: UserProfile, ) -> None: device_id = DeviceID.new() try: now = pendulum_now() user_certificate = UserCertificateContent( author=author.device_id, timestamp=now, user_id=device_id.user_id, human_handle=human_handle, public_key=self._public_key, profile=profile, ) redacted_user_certificate = user_certificate.evolve( human_handle=None) 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) 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 rep = await self._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 InviteError(f"Cannot create device: {rep}") try: payload = InviteUserConfirmation( device_id=device_id, device_label=device_label, human_handle=human_handle, profile=profile, 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)