diff --git a/lib/utils/crypto-utils.js b/lib/utils/crypto-utils.js
--- a/lib/utils/crypto-utils.js
+++ b/lib/utils/crypto-utils.js
@@ -3,13 +3,21 @@
 import t from 'tcomb';
 import { type TInterface } from 'tcomb';
 
+import { getConfig } from './config.js';
 import { primaryIdentityPublicKeyRegex } from './siwe-utils.js';
 import { tRegex, tShape } from './validation-utils.js';
+import type { AuthMetadata } from '../shared/identity-client-context.js';
+import type { ClientMessageToDevice } from '../tunnelbroker/tunnelbroker-context.js';
 import type {
   IdentityKeysBlob,
   OLMIdentityKeys,
   SignedIdentityKeysBlob,
 } from '../types/crypto-types';
+import type { IdentityServiceClient } from '../types/identity-service-types';
+import {
+  type OutboundSessionCreation,
+  peerToPeerMessageTypes,
+} from '../types/tunnelbroker/peer-to-peer-message-types.js';
 
 const signedIdentityKeysBlobValidator: TInterface<SignedIdentityKeysBlob> =
   tShape({
@@ -27,4 +35,82 @@
   notificationIdentityPublicKeys: olmIdentityKeysValidator,
 });
 
-export { signedIdentityKeysBlobValidator, identityKeysBlobValidator };
+async function getContentSigningKey(): Promise<string> {
+  const { olmAPI } = getConfig();
+  await olmAPI.initializeCryptoAccount();
+  const {
+    primaryIdentityPublicKeys: { ed25519 },
+  } = await olmAPI.getUserPublicKey();
+  return ed25519;
+}
+
+async function createOlmSessionsWithOwnDevices(
+  authMetadata: AuthMetadata,
+  identityClient: IdentityServiceClient,
+  sendMessage: (message: ClientMessageToDevice) => Promise<void>,
+): Promise<void> {
+  const { olmAPI } = getConfig();
+  const { userID, deviceID, accessToken } = authMetadata;
+  await olmAPI.initializeCryptoAccount();
+
+  if (!userID || !deviceID || !accessToken) {
+    throw new Error('CommServicesAuthMetadata is missing');
+  }
+
+  const keysResponse = await identityClient.getOutboundKeysForUser(userID);
+
+  for (const deviceKeys of keysResponse) {
+    const { keys } = deviceKeys;
+    if (!keys) {
+      console.log(`Keys missing for device ${deviceKeys.deviceID}`);
+      continue;
+    }
+
+    const { primaryIdentityPublicKeys } = keys.identityKeysBlob;
+
+    if (primaryIdentityPublicKeys.ed25519 === deviceID) {
+      continue;
+    }
+    const recipientDeviceID = primaryIdentityPublicKeys.ed25519;
+
+    if (!keys.contentInitializationInfo.oneTimeKey) {
+      console.log(`One-time key is missing for device ${recipientDeviceID}`);
+      continue;
+    }
+    try {
+      const encryptedContent = await olmAPI.contentOutboundSessionCreator(
+        primaryIdentityPublicKeys,
+        keys.contentInitializationInfo,
+      );
+
+      const sessionCreationMessage: OutboundSessionCreation = {
+        type: peerToPeerMessageTypes.OUTBOUND_SESSION_CREATION,
+        senderInfo: {
+          userID,
+          deviceID,
+        },
+        encryptedContent,
+      };
+
+      await sendMessage({
+        deviceID: recipientDeviceID,
+        payload: JSON.stringify(sessionCreationMessage),
+      });
+      console.log(
+        `Request to create a session with device ${recipientDeviceID} sent.`,
+      );
+    } catch (e) {
+      console.log(
+        'Error creating outbound session with ' +
+          `device ${recipientDeviceID}: ${e.message}`,
+      );
+    }
+  }
+}
+
+export {
+  signedIdentityKeysBlobValidator,
+  identityKeysBlobValidator,
+  getContentSigningKey,
+  createOlmSessionsWithOwnDevices,
+};
diff --git a/native/account/siwe-panel.react.js b/native/account/siwe-panel.react.js
--- a/native/account/siwe-panel.react.js
+++ b/native/account/siwe-panel.react.js
@@ -18,6 +18,7 @@
 import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js';
 import type { SIWEWebViewMessage, SIWEResult } from 'lib/types/siwe-types.js';
 import { useLegacyAshoatKeyserverCall } from 'lib/utils/action-utils.js';
+import { getContentSigningKey } from 'lib/utils/crypto-utils.js';
 import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js';
 import { usingCommServicesAccessToken } from 'lib/utils/services-utils.js';
 
@@ -26,7 +27,6 @@
 import type { BottomSheetRef } from '../types/bottom-sheet.js';
 import { UnknownErrorAlertDetails } from '../utils/alert-messages.js';
 import Alert from '../utils/alert.js';
-import { getContentSigningKey } from '../utils/crypto-utils.js';
 import { defaultLandingURLPrefix } from '../utils/url-utils.js';
 
 const commSIWE = `${defaultLandingURLPrefix}/siwe`;
diff --git a/native/backup/use-client-backup.js b/native/backup/use-client-backup.js
--- a/native/backup/use-client-backup.js
+++ b/native/backup/use-client-backup.js
@@ -3,11 +3,11 @@
 import * as React from 'react';
 
 import { isLoggedIn } from 'lib/selectors/user-selectors.js';
+import { getContentSigningKey } from 'lib/utils/crypto-utils.js';
 
 import { fetchNativeKeychainCredentials } from '../account/native-credentials.js';
 import { commCoreModule } from '../native-modules.js';
 import { useSelector } from '../redux/redux-utils.js';
-import { getContentSigningKey } from '../utils/crypto-utils.js';
 
 type ClientBackup = {
   +uploadBackupProtocol: () => Promise<void>,
diff --git a/native/identity-service/identity-service-context-provider.react.js b/native/identity-service/identity-service-context-provider.react.js
--- a/native/identity-service/identity-service-context-provider.react.js
+++ b/native/identity-service/identity-service-context-provider.react.js
@@ -25,12 +25,12 @@
   deviceOlmInboundKeysValidator,
   userDeviceOlmInboundKeysValidator,
 } from 'lib/types/identity-service-types.js';
+import { getContentSigningKey } from 'lib/utils/crypto-utils.js';
 import { assertWithValidator } from 'lib/utils/validation-utils.js';
 
 import { getCommServicesAuthMetadataEmitter } from '../event-emitters/csa-auth-metadata-emitter.js';
 import { commCoreModule, commRustModule } from '../native-modules.js';
 import { useSelector } from '../redux/redux-utils.js';
-import { getContentSigningKey } from '../utils/crypto-utils.js';
 
 type Props = {
   +children: React.Node,
diff --git a/native/profile/tunnelbroker-menu.react.js b/native/profile/tunnelbroker-menu.react.js
--- a/native/profile/tunnelbroker-menu.react.js
+++ b/native/profile/tunnelbroker-menu.react.js
@@ -5,24 +5,25 @@
 import { Text, View } from 'react-native';
 import { ScrollView } from 'react-native-gesture-handler';
 
+import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
 import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js';
 import type { TunnelbrokerMessage } from 'lib/types/tunnelbroker/messages.js';
 import {
   type EncryptedMessage,
   peerToPeerMessageTypes,
 } from 'lib/types/tunnelbroker/peer-to-peer-message-types.js';
+import {
+  createOlmSessionsWithOwnDevices,
+  getContentSigningKey,
+} from 'lib/utils/crypto-utils.js';
 
 import type { ProfileNavigationProp } from './profile.react.js';
 import Button from '../components/button.react.js';
 import TextInput from '../components/text-input.react.js';
-import { commCoreModule } from '../native-modules.js';
+import { olmAPI } from '../crypto/olm-api.js';
 import type { NavigationRoute } from '../navigation/route-names.js';
 import { useSelector } from '../redux/redux-utils.js';
 import { useColors, useStyles } from '../themes/colors.js';
-import {
-  createOlmSessionsWithOwnDevices,
-  getContentSigningKey,
-} from '../utils/crypto-utils.js';
 
 type Props = {
   +navigation: ProfileNavigationProp<'TunnelbrokerMenu'>,
@@ -35,6 +36,7 @@
   const currentUserID = useSelector(
     state => state.currentUserInfo && state.currentUserInfo.id,
   );
+  const identityContext = React.useContext(IdentityClientContext);
 
   const { connected, addListener, sendMessage, removeListener } =
     useTunnelbroker();
@@ -61,20 +63,28 @@
   }, [message, recipient, sendMessage]);
 
   const onCreateSessions = React.useCallback(async () => {
+    if (!identityContext) {
+      return;
+    }
+    const authMetadata = await identityContext.getAuthMetadata();
     try {
-      await createOlmSessionsWithOwnDevices(sendMessage);
+      await createOlmSessionsWithOwnDevices(
+        authMetadata,
+        identityContext.identityClient,
+        sendMessage,
+      );
     } catch (e) {
       console.log(`Error creating olm sessions with own devices: ${e.message}`);
     }
-  }, [sendMessage]);
+  }, [identityContext, sendMessage]);
 
   const onSendEncryptedMessage = React.useCallback(async () => {
     try {
       if (!currentUserID) {
         return;
       }
-      await commCoreModule.initializeCryptoAccount();
-      const encrypted = await commCoreModule.encrypt(
+      await olmAPI.initializeCryptoAccount();
+      const encrypted = await olmAPI.encrypt(
         `Encrypted message to ${recipient}`,
         recipient,
       );
diff --git a/native/qr-code/qr-code-screen.react.js b/native/qr-code/qr-code-screen.react.js
--- a/native/qr-code/qr-code-screen.react.js
+++ b/native/qr-code/qr-code-screen.react.js
@@ -15,6 +15,7 @@
   NonceChallenge,
   SignedMessage,
 } from 'lib/types/identity-service-types.js';
+import { getContentSigningKey } from 'lib/utils/crypto-utils.js';
 
 import type { QRCodeSignInNavigationProp } from './qr-code-sign-in-navigator.react.js';
 import {
@@ -26,7 +27,6 @@
 import { useStyles } from '../themes/colors.js';
 import * as AES from '../utils/aes-crypto-module.js';
 import Alert from '../utils/alert.js';
-import { getContentSigningKey } from '../utils/crypto-utils.js';
 
 type QRCodeScreenProps = {
   +navigation: QRCodeSignInNavigationProp<'QRCodeScreen'>,
diff --git a/native/utils/crypto-utils.js b/native/utils/crypto-utils.js
--- a/native/utils/crypto-utils.js
+++ b/native/utils/crypto-utils.js
@@ -1,18 +1,9 @@
 // @flow
 
-import { type ClientMessageToDevice } from 'lib/tunnelbroker/tunnelbroker-context.js';
-import type {
-  IdentityKeysBlob,
-  OLMIdentityKeys,
-} from 'lib/types/crypto-types.js';
-import type { OutboundKeyInfoResponse } from 'lib/types/identity-service-types';
+import type { OLMIdentityKeys } from 'lib/types/crypto-types.js';
 import type { OlmSessionInitializationInfo } from 'lib/types/request-types.js';
-import {
-  type OutboundSessionCreation,
-  peerToPeerMessageTypes,
-} from 'lib/types/tunnelbroker/peer-to-peer-message-types.js';
 
-import { commCoreModule, commRustModule } from '../native-modules.js';
+import { commCoreModule } from '../native-modules.js';
 
 function nativeNotificationsSessionCreator(
   notificationsIdentityKeys: OLMIdentityKeys,
@@ -30,14 +21,6 @@
   );
 }
 
-async function getContentSigningKey(): Promise<string> {
-  await commCoreModule.initializeCryptoAccount();
-  const {
-    primaryIdentityPublicKeys: { ed25519 },
-  } = await commCoreModule.getUserPublicKey();
-  return ed25519;
-}
-
 function nativeOutboundContentSessionCreator(
   contentIdentityKeys: OLMIdentityKeys,
   contentInitializationInfo: OlmSessionInitializationInfo,
@@ -58,75 +41,7 @@
   );
 }
 
-async function createOlmSessionsWithOwnDevices(
-  sendMessage: (message: ClientMessageToDevice) => Promise<void>,
-): Promise<void> {
-  const authMetadata = await commCoreModule.getCommServicesAuthMetadata();
-  const { userID, deviceID, accessToken } = authMetadata;
-  if (!userID || !deviceID || !accessToken) {
-    throw new Error('CommServicesAuthMetadata is missing');
-  }
-
-  await commCoreModule.initializeCryptoAccount();
-  const keysResponse = await commRustModule.getOutboundKeysForUser(
-    userID,
-    deviceID,
-    accessToken,
-    userID,
-  );
-
-  const outboundKeys: OutboundKeyInfoResponse[] = JSON.parse(keysResponse);
-
-  for (const deviceKeys: OutboundKeyInfoResponse of outboundKeys) {
-    const keysPayload: IdentityKeysBlob = JSON.parse(deviceKeys.payload);
-
-    if (keysPayload.primaryIdentityPublicKeys.ed25519 === deviceID) {
-      continue;
-    }
-    const recipientDeviceID = keysPayload.primaryIdentityPublicKeys.ed25519;
-    if (!deviceKeys.oneTimeContentPrekey) {
-      console.log(`One-time key is missing for device ${recipientDeviceID}`);
-      continue;
-    }
-    try {
-      const encryptedContent = await nativeOutboundContentSessionCreator(
-        keysPayload.primaryIdentityPublicKeys,
-        {
-          prekey: deviceKeys.contentPrekey,
-          prekeySignature: deviceKeys.contentPrekeySignature,
-          oneTimeKey: deviceKeys.oneTimeContentPrekey,
-        },
-        recipientDeviceID,
-      );
-
-      const sessionCreationMessage: OutboundSessionCreation = {
-        type: peerToPeerMessageTypes.OUTBOUND_SESSION_CREATION,
-        senderInfo: {
-          userID,
-          deviceID,
-        },
-        encryptedContent,
-      };
-
-      await sendMessage({
-        deviceID: recipientDeviceID,
-        payload: JSON.stringify(sessionCreationMessage),
-      });
-      console.log(
-        `Request to create a session with device ${recipientDeviceID} sent.`,
-      );
-    } catch (e) {
-      console.log(
-        'Error creating outbound session with ' +
-          `device ${recipientDeviceID}: ${e.message}`,
-      );
-    }
-  }
-}
-
 export {
-  getContentSigningKey,
   nativeNotificationsSessionCreator,
-  createOlmSessionsWithOwnDevices,
   nativeOutboundContentSessionCreator,
 };
diff --git a/web/settings/account-settings.react.js b/web/settings/account-settings.react.js
--- a/web/settings/account-settings.react.js
+++ b/web/settings/account-settings.react.js
@@ -7,7 +7,9 @@
 import SWMansionIcon from 'lib/components/swmansion-icon.react.js';
 import { useStringForUser } from 'lib/hooks/ens-cache.js';
 import { accountHasPassword } from 'lib/shared/account-utils.js';
+import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
 import { useTunnelbroker } from 'lib/tunnelbroker/tunnelbroker-context.js';
+import { createOlmSessionsWithOwnDevices } from 'lib/utils/crypto-utils.js';
 import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js';
 
 import css from './account-settings.css';
@@ -30,6 +32,7 @@
     () => dispatchActionPromise(logOutActionTypes, sendLogoutRequest()),
     [dispatchActionPromise, sendLogoutRequest],
   );
+  const identityContext = React.useContext(IdentityClientContext);
 
   const { pushModal, popModal } = useModalContext();
   const showPasswordChangeModal = React.useCallback(
@@ -77,6 +80,22 @@
     [addListener, popModal, pushModal, removeListener],
   );
 
+  const onCreateOlmSessions = React.useCallback(async () => {
+    if (!identityContext) {
+      return;
+    }
+    const authMetadata = await identityContext.getAuthMetadata();
+    try {
+      await createOlmSessionsWithOwnDevices(
+        authMetadata,
+        identityContext.identityClient,
+        sendMessage,
+      );
+    } catch (e) {
+      console.log(`Error creating olm sessions with own devices: ${e.message}`);
+    }
+  }, [identityContext, sendMessage]);
+
   const openBackupTestRestoreModal = React.useCallback(
     () => pushModal(<BackupTestRestoreModal onClose={popModal} />),
     [popModal, pushModal],
@@ -147,6 +166,12 @@
                 <p className={css.buttonText}>Show list</p>
               </Button>
             </li>
+            <li>
+              <span>Create session with own devices</span>
+              <Button variant="text" onClick={onCreateOlmSessions}>
+                <p className={css.buttonText}>Create</p>
+              </Button>
+            </li>
           </ul>
         </div>
       </div>
diff --git a/web/settings/tunnelbroker-test.react.js b/web/settings/tunnelbroker-test.react.js
--- a/web/settings/tunnelbroker-test.react.js
+++ b/web/settings/tunnelbroker-test.react.js
@@ -4,11 +4,18 @@
 import * as React from 'react';
 
 import { type ClientMessageToDevice } from 'lib/tunnelbroker/tunnelbroker-context.js';
+import {
+  type EncryptedMessage,
+  peerToPeerMessageTypes,
+} from 'lib/types/tunnelbroker/peer-to-peer-message-types.js';
+import { getContentSigningKey } from 'lib/utils/crypto-utils.js';
 
 import css from './tunnelbroker-test.css';
 import Button from '../components/button.react.js';
+import { olmAPI } from '../crypto/olm-api.js';
 import Input from '../modals/input.react.js';
 import Modal from '../modals/modal.react.js';
+import { useSelector } from '../redux/redux-utils.js';
 
 type Props = {
   +sendMessage: (message: ClientMessageToDevice) => Promise<void>,
@@ -24,6 +31,10 @@
   const recipientInput = React.useRef<?HTMLInputElement>(null);
   const messageInput = React.useRef<?HTMLInputElement>(null);
 
+  const currentUserID = useSelector(
+    state => state.currentUserInfo && state.currentUserInfo.id,
+  );
+
   const onSubmit = React.useCallback(
     async (event: SyntheticEvent<HTMLButtonElement>) => {
       event.preventDefault();
@@ -39,6 +50,42 @@
     [message, recipient, sendMessage],
   );
 
+  const onSubmitEncrypted = React.useCallback(
+    async (event: SyntheticEvent<HTMLButtonElement>) => {
+      event.preventDefault();
+
+      if (!currentUserID) {
+        return;
+      }
+
+      setLoading(true);
+      try {
+        await olmAPI.initializeCryptoAccount();
+        const encrypted = await olmAPI.encrypt(
+          `Encrypted message to ${recipient}`,
+          recipient,
+        );
+        const deviceID = await getContentSigningKey();
+        const encryptedMessage: EncryptedMessage = {
+          type: peerToPeerMessageTypes.ENCRYPTED_MESSAGE,
+          senderInfo: {
+            deviceID,
+            userID: currentUserID,
+          },
+          encryptedContent: encrypted,
+        };
+        await sendMessage({
+          deviceID: recipient,
+          payload: JSON.stringify(encryptedMessage),
+        });
+      } catch (e) {
+        setErrorMessage(e.message);
+      }
+      setLoading(false);
+    },
+    [currentUserID, recipient, sendMessage],
+  );
+
   let errorMsg;
   if (errorMessage) {
     errorMsg = <div className={css.modalError}>{errorMessage}</div>;
@@ -86,6 +133,17 @@
           </Button>
           {errorMsg}
         </div>
+        <div className={css.footer}>
+          <Button
+            type="submit"
+            variant="filled"
+            onClick={onSubmitEncrypted}
+            disabled={!recipient || loading}
+          >
+            Send Encrypted Message
+          </Button>
+          {errorMsg}
+        </div>
       </div>
     </Modal>
   );