def _create_new_user_certificates( author: LocalDevice, device_label: Optional[DeviceLabel], human_handle: Optional[HumanHandle], profile: UserProfile, public_key: PublicKey, verify_key: VerifyKey, ) -> Tuple[bytes, bytes, bytes, bytes, InviteUserConfirmation]: """Helper to prepare the creation of a new user.""" device_id = DeviceID.new() try: timestamp = author.timestamp() user_certificate = UserCertificateContent( author=author.device_id, timestamp=timestamp, user_id=device_id.user_id, human_handle=human_handle, public_key=public_key, profile=profile, ) redacted_user_certificate = user_certificate.evolve(human_handle=None) device_certificate = DeviceCertificateContent( author=author.device_id, timestamp=timestamp, device_id=device_id, device_label=device_label, verify_key=verify_key, ) redacted_device_certificate = device_certificate.evolve( device_label=None) user_certificate = user_certificate.dump_and_sign(author.signing_key) redacted_user_certificate = redacted_user_certificate.dump_and_sign( author.signing_key) device_certificate = device_certificate.dump_and_sign( author.signing_key) redacted_device_certificate = redacted_device_certificate.dump_and_sign( author.signing_key) except DataError as exc: raise InviteError( f"Cannot generate device certificate: {exc}") from exc invite_user_confirmation = InviteUserConfirmation( device_id=device_id, device_label=device_label, human_handle=human_handle, profile=profile, root_verify_key=author.root_verify_key, ) return ( user_certificate, redacted_user_certificate, device_certificate, redacted_device_certificate, invite_user_confirmation, )
async def do_claim_user( self, requested_device_label: Optional[DeviceLabel], requested_human_handle: Optional[HumanHandle], ) -> LocalDevice: # User&device keys are generated here and kept in memory until the end of # the enrollment process. This mean we can lost it if something goes wrong. # This has no impact until step 4 (somewhere between data exchange and # confirmation exchange steps) where greeter upload our certificates in # the server. # This is considered acceptable given 1) the error window is small and # 2) if this occurs the inviter can revoke the user and retry the # enrollment process to fix this private_key = PrivateKey.generate() signing_key = SigningKey.generate() try: payload = InviteUserData( requested_device_label=requested_device_label, requested_human_handle=requested_human_handle, public_key=private_key.public_key, verify_key=signing_key.verify_key, ).dump_and_encrypt(key=self._shared_secret_key) except DataError as exc: raise InviteError( "Cannot generate InviteUserData payload") from exc rep = await self._cmds.invite_4_claimer_communicate(payload=payload) _check_rep(rep, step_name="step 4 (data exchange)") rep = await self._cmds.invite_4_claimer_communicate(payload=b"") _check_rep(rep, step_name="step 4 (confirmation exchange)") try: confirmation = InviteUserConfirmation.decrypt_and_load( rep["payload"], key=self._shared_secret_key) except DataError as exc: raise InviteError( "Invalid InviteUserConfirmation payload provided by peer" ) from exc organization_addr = BackendOrganizationAddr.build( backend_addr=self._cmds.addr.get_backend_addr(), organization_id=self._cmds.addr.organization_id, root_verify_key=confirmation.root_verify_key, ) new_device = generate_new_device( organization_addr=organization_addr, device_id=confirmation.device_id, device_label=confirmation.device_label, human_handle=confirmation.human_handle, profile=confirmation.profile, private_key=private_key, signing_key=signing_key, ) return new_device
async def do_claim_user( self, requested_device_label: Optional[str], requested_human_handle: Optional[HumanHandle]) -> LocalDevice: private_key = PrivateKey.generate() signing_key = SigningKey.generate() try: payload = InviteUserData( requested_device_label=requested_device_label, requested_human_handle=requested_human_handle, public_key=private_key.public_key, verify_key=signing_key.verify_key, ).dump_and_encrypt(key=self._shared_secret_key) except DataError as exc: raise InviteError( "Cannot generate InviteUserData payload") from exc rep = await self._cmds.invite_4_claimer_communicate(payload=payload) if rep["status"] == "invalid_state": raise InvitePeerResetError() elif rep["status"] != "ok": raise InviteError( f"Backend error during step 4 (data exchange): {rep}") rep = await self._cmds.invite_4_claimer_communicate(payload=b"") if rep["status"] == "invalid_state": raise InvitePeerResetError() elif rep["status"] != "ok": raise InviteError( f"Backend error during step 4 (confirmation exchange): {rep}") try: confirmation = InviteUserConfirmation.decrypt_and_load( rep["payload"], key=self._shared_secret_key) except DataError as exc: raise InviteError( "Invalid InviteUserConfirmation payload provided by peer" ) from exc organization_addr = BackendOrganizationAddr.build( backend_addr=self._cmds.addr, organization_id=self._cmds.addr.organization_id, root_verify_key=confirmation.root_verify_key, ) new_device = generate_new_device( organization_addr=organization_addr, device_id=confirmation.device_id, device_label=confirmation.device_label, human_handle=confirmation.human_handle, profile=confirmation.profile, private_key=private_key, signing_key=signing_key, ) return new_device
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)