diff --git a/keyserver/src/push/crypto.js b/keyserver/src/push/crypto.js
--- a/keyserver/src/push/crypto.js
+++ b/keyserver/src/push/crypto.js
@@ -1,6 +1,5 @@
 // @flow
 
-import type { EncryptResult } from '@commapp/olm';
 import apn from '@parse/node-apn';
 import crypto from 'crypto';
 import invariant from 'invariant';
@@ -18,16 +17,17 @@
   AndroidNotificationRescind,
   NotificationTargetDevice,
   SenderDeviceDescriptor,
+  EncryptedNotifUtilsAPI,
 } from 'lib/types/notif-types.js';
 import { toBase64URL } from 'lib/utils/base64.js';
 
-import { encryptAndUpdateOlmSession } from '../updaters/olm-session-updater.js';
 import { encrypt, generateKey } from '../utils/aes-crypto-utils.js';
 import { getOlmUtility } from '../utils/olm-utils.js';
 
 async function encryptAPNsNotification(
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   cookieID: string,
-  senderDeviceID: SenderDeviceDescriptor,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   notification: apn.Notification,
   codeVersion?: ?number,
   notificationSizeValidator?: apn.Notification => boolean,
@@ -71,33 +71,26 @@
 
     let dbPersistCondition;
     if (notificationSizeValidator) {
-      dbPersistCondition = ({
-        serializedPayload,
-      }: {
-        +[string]: EncryptResult,
-      }) => {
+      dbPersistCondition = (serializedPayload: string) => {
         const notifCopy = _cloneDeep(encryptedNotification);
-        notifCopy.payload.encryptedPayload = serializedPayload.body;
+        notifCopy.payload.encryptedPayload = serializedPayload;
         return notificationSizeValidator(notifCopy);
       };
     }
     const {
-      encryptedMessages: { serializedPayload },
-      dbPersistConditionViolated,
+      encryptedData: serializedPayload,
+      sizeLimitViolated: dbPersistConditionViolated,
       encryptionOrder,
-    } = await encryptAndUpdateOlmSession(
+    } = await encryptedNotifUtilsAPI.encryptSerializedNotifPayload(
       cookieID,
-      'notifications',
-      {
-        serializedPayload: unencryptedSerializedPayload,
-      },
+      unencryptedSerializedPayload,
       dbPersistCondition,
     );
 
     encryptedNotification.payload.encryptedPayload = serializedPayload.body;
     encryptedNotification.payload = {
-      ...senderDeviceID,
       ...encryptedNotification.payload,
+      ...senderDeviceDescriptor,
     };
 
     if (codeVersion && codeVersion >= 254 && codeVersion % 2 === 0) {
@@ -140,8 +133,9 @@
 }
 
 async function encryptAndroidNotificationPayload<T>(
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   cookieID: string,
-  senderDeviceID: SenderDeviceDescriptor,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   unencryptedPayload: T,
   payloadSizeValidator?: (
     T | $ReadOnly<{ ...SenderDeviceDescriptor, +encryptedPayload: string }>,
@@ -166,33 +160,27 @@
 
     let dbPersistCondition;
     if (payloadSizeValidator) {
-      dbPersistCondition = ({
-        serializedPayload,
-      }: {
-        +[string]: EncryptResult,
-      }) =>
+      dbPersistCondition = (serializedPayload: string) =>
         payloadSizeValidator({
-          encryptedPayload: serializedPayload.body,
-          ...senderDeviceID,
+          encryptedPayload: serializedPayload,
+          ...senderDeviceDescriptor,
         });
     }
 
     const {
-      encryptedMessages: { serializedPayload },
-      dbPersistConditionViolated,
+      encryptedData: serializedPayload,
+      sizeLimitViolated: dbPersistConditionViolated,
       encryptionOrder,
-    } = await encryptAndUpdateOlmSession(
+    } = await encryptedNotifUtilsAPI.encryptSerializedNotifPayload(
       cookieID,
-      'notifications',
-      {
-        serializedPayload: unencryptedSerializedPayload,
-      },
+      unencryptedSerializedPayload,
       dbPersistCondition,
     );
+
     return {
       resultPayload: {
         encryptedPayload: serializedPayload.body,
-        ...senderDeviceID,
+        ...senderDeviceDescriptor,
       },
       payloadSizeExceeded: !!dbPersistConditionViolated,
       encryptionOrder,
@@ -213,7 +201,8 @@
 }
 
 async function encryptAndroidVisualNotification(
-  senderDeviceID: SenderDeviceDescriptor,
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   cookieID: string,
   notification: AndroidVisualNotification,
   notificationSizeValidator?: AndroidVisualNotification => boolean,
@@ -249,8 +238,9 @@
   }
   const { resultPayload, payloadSizeExceeded, encryptionOrder } =
     await encryptAndroidNotificationPayload(
+      encryptedNotifUtilsAPI,
       cookieID,
-      senderDeviceID,
+      senderDeviceDescriptor,
       unencryptedPayload,
       payloadSizeValidator,
     );
@@ -267,8 +257,9 @@
 }
 
 async function encryptAndroidSilentNotification(
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   cookieID: string,
-  senderDeviceID: SenderDeviceDescriptor,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
 ): Promise<AndroidNotificationRescind | AndroidBadgeOnlyNotification> {
   // We don't validate payload size for rescind
@@ -276,8 +267,9 @@
   // never exceed any FCM limit
   const { ...unencryptedPayload } = notification.data;
   const { resultPayload } = await encryptAndroidNotificationPayload(
+    encryptedNotifUtilsAPI,
     cookieID,
-    senderDeviceID,
+    senderDeviceDescriptor,
     unencryptedPayload,
   );
   if (resultPayload.encryptedPayload) {
@@ -300,8 +292,9 @@
 }
 
 async function encryptBasicPayload<T>(
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   cookieID: string,
-  senderDeviceID: SenderDeviceDescriptor,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   basicPayload: T,
 ): Promise<
   | $ReadOnly<{
@@ -318,15 +311,14 @@
   }
 
   try {
-    const {
-      encryptedMessages: { serializedPayload },
-      encryptionOrder,
-    } = await encryptAndUpdateOlmSession(cookieID, 'notifications', {
-      serializedPayload: unencryptedSerializedPayload,
-    });
+    const { encryptedData: serializedPayload, encryptionOrder } =
+      await encryptedNotifUtilsAPI.encryptSerializedNotifPayload(
+        cookieID,
+        unencryptedSerializedPayload,
+      );
 
     return {
-      ...senderDeviceID,
+      ...senderDeviceDescriptor,
       encryptedPayload: serializedPayload.body,
       encryptionOrder,
     };
@@ -340,15 +332,17 @@
 }
 
 async function encryptWebNotification(
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   cookieID: string,
-  senderDeviceID: SenderDeviceDescriptor,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   notification: PlainTextWebNotification,
 ): Promise<{ +notification: WebNotification, +encryptionOrder?: number }> {
   const { id, ...payloadSansId } = notification;
   const { encryptionOrder, ...encryptionResult } =
     await encryptBasicPayload<PlainTextWebNotificationPayload>(
+      encryptedNotifUtilsAPI,
       cookieID,
-      senderDeviceID,
+      senderDeviceDescriptor,
       payloadSansId,
     );
 
@@ -359,14 +353,16 @@
 }
 
 async function encryptWNSNotification(
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
   cookieID: string,
-  senderDeviceID: SenderDeviceDescriptor,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   notification: PlainTextWNSNotification,
 ): Promise<{ +notification: WNSNotification, +encryptionOrder?: number }> {
   const { encryptionOrder, ...encryptionResult } =
     await encryptBasicPayload<PlainTextWNSNotification>(
+      encryptedNotifUtilsAPI,
       cookieID,
-      senderDeviceID,
+      senderDeviceDescriptor,
       notification,
     );
   return {
@@ -376,7 +372,8 @@
 }
 
 function prepareEncryptedAPNsNotifications(
-  senderDeviceID: SenderDeviceDescriptor,
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: apn.Notification,
   codeVersion?: ?number,
@@ -394,8 +391,9 @@
   const notificationPromises = devices.map(
     async ({ cookieID, deviceToken, blobHolder }) => {
       const notif = await encryptAPNsNotification(
+        encryptedNotifUtilsAPI,
         cookieID,
-        senderDeviceID,
+        senderDeviceDescriptor,
         notification,
         codeVersion,
         notificationSizeValidator,
@@ -408,7 +406,8 @@
 }
 
 function prepareEncryptedIOSNotificationRescind(
-  senderDeviceID: SenderDeviceDescriptor,
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: apn.Notification,
   codeVersion?: ?number,
@@ -422,8 +421,9 @@
   const notificationPromises = devices.map(
     async ({ deviceToken, cookieID }) => {
       const { notification: notif } = await encryptAPNsNotification(
+        encryptedNotifUtilsAPI,
         cookieID,
-        senderDeviceID,
+        senderDeviceDescriptor,
         notification,
         codeVersion,
       );
@@ -434,7 +434,8 @@
 }
 
 function prepareEncryptedAndroidVisualNotifications(
-  senderDeviceID: SenderDeviceDescriptor,
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: AndroidVisualNotification,
   notificationSizeValidator?: (
@@ -452,7 +453,8 @@
   const notificationPromises = devices.map(
     async ({ deviceToken, cookieID, blobHolder }) => {
       const notif = await encryptAndroidVisualNotification(
-        senderDeviceID,
+        encryptedNotifUtilsAPI,
+        senderDeviceDescriptor,
         cookieID,
         notification,
         notificationSizeValidator,
@@ -465,7 +467,8 @@
 }
 
 function prepareEncryptedAndroidSilentNotifications(
-  senderDeviceID: SenderDeviceDescriptor,
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: AndroidNotificationRescind | AndroidBadgeOnlyNotification,
 ): Promise<
@@ -479,8 +482,9 @@
   const notificationPromises = devices.map(
     async ({ deviceToken, cookieID }) => {
       const notif = await encryptAndroidSilentNotification(
+        encryptedNotifUtilsAPI,
         cookieID,
-        senderDeviceID,
+        senderDeviceDescriptor,
         notification,
       );
       return { deviceToken, cookieID, notification: notif };
@@ -490,7 +494,8 @@
 }
 
 function prepareEncryptedWebNotifications(
-  senderDeviceID: SenderDeviceDescriptor,
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: PlainTextWebNotification,
 ): Promise<
@@ -503,8 +508,9 @@
   const notificationPromises = devices.map(
     async ({ deviceToken, cookieID }) => {
       const notif = await encryptWebNotification(
+        encryptedNotifUtilsAPI,
         cookieID,
-        senderDeviceID,
+        senderDeviceDescriptor,
         notification,
       );
       return { ...notif, deviceToken };
@@ -514,7 +520,8 @@
 }
 
 function prepareEncryptedWNSNotifications(
-  senderDeviceID: SenderDeviceDescriptor,
+  encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   notification: PlainTextWNSNotification,
 ): Promise<
@@ -527,8 +534,9 @@
   const notificationPromises = devices.map(
     async ({ deviceToken, cookieID }) => {
       const notif = await encryptWNSNotification(
+        encryptedNotifUtilsAPI,
         cookieID,
-        senderDeviceID,
+        senderDeviceDescriptor,
         notification,
       );
       return { ...notif, deviceToken };
diff --git a/keyserver/src/push/encrypted-notif-utils-api.js b/keyserver/src/push/encrypted-notif-utils-api.js
new file mode 100644
--- /dev/null
+++ b/keyserver/src/push/encrypted-notif-utils-api.js
@@ -0,0 +1,49 @@
+// @flow
+
+import type { EncryptResult } from '@commapp/olm';
+
+import type { EncryptedNotifUtilsAPI } from 'lib/types/notif-types.js';
+
+import { blobServiceUpload } from './utils.js';
+import { encryptAndUpdateOlmSession } from '../updaters/olm-session-updater.js';
+
+const encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI = {
+  encryptSerializedNotifPayload: async (
+    cryptoID: string,
+    unencryptedPayload: string,
+    encryptedPayloadSizeValidator?: (encryptedPayload: string) => boolean,
+  ) => {
+    let dbPersistCondition;
+    if (encryptedPayloadSizeValidator) {
+      dbPersistCondition = ({
+        serializedPayload,
+      }: {
+        +[string]: EncryptResult,
+      }) => encryptedPayloadSizeValidator(serializedPayload.body);
+    }
+
+    const {
+      encryptedMessages: { serializedPayload },
+      dbPersistConditionViolated,
+      encryptionOrder,
+    } = await encryptAndUpdateOlmSession(
+      cryptoID,
+      'notifications',
+      {
+        serializedPayload: unencryptedPayload,
+      },
+      dbPersistCondition,
+    );
+
+    return {
+      encryptedData: serializedPayload,
+      sizeLimitViolated: dbPersistConditionViolated,
+      encryptionOrder,
+    };
+  },
+  uploadLargeNotifPayload: blobServiceUpload,
+  getNotifByteSize: (serializedPayload: string) =>
+    Buffer.byteLength(serializedPayload),
+};
+
+export default encryptedNotifUtilsAPI;
diff --git a/keyserver/src/push/rescind.js b/keyserver/src/push/rescind.js
--- a/keyserver/src/push/rescind.js
+++ b/keyserver/src/push/rescind.js
@@ -10,6 +10,7 @@
   NotificationTargetDevice,
   TargetedAndroidNotification,
   SenderDeviceDescriptor,
+  EncryptedNotifUtilsAPI,
 } from 'lib/types/notif-types.js';
 import { threadSubscriptions } from 'lib/types/subscription-types.js';
 import { threadPermissions } from 'lib/types/thread-permission-types.js';
@@ -20,6 +21,7 @@
   prepareEncryptedAndroidSilentNotifications,
   prepareEncryptedIOSNotificationRescind,
 } from './crypto.js';
+import encryptedNotifUtilsAPI from './encrypted-notif-utils-api.js';
 import { getAPNsNotificationTopic } from './providers.js';
 import type { TargetedAPNsNotification } from './types.js';
 import {
@@ -275,12 +277,14 @@
 }
 
 async function conditionallyEncryptNotification<T>(
-  senderDeviceID: SenderDeviceDescriptor,
+  encryptedNotifUtilsAPIInstance: EncryptedNotifUtilsAPI,
+  senderDeviceDescriptor: SenderDeviceDescriptor,
   notification: T,
   codeVersion: ?number,
   devices: $ReadOnlyArray<NotificationTargetDevice>,
   encryptCallback: (
-    senderDeviceID: SenderDeviceDescriptor,
+    encryptedNotifUtilsAPI: EncryptedNotifUtilsAPI,
+    senderDeviceDescriptor: SenderDeviceDescriptor,
     devices: $ReadOnlyArray<NotificationTargetDevice>,
     notification: T,
     codeVersion?: ?number,
@@ -301,7 +305,8 @@
     }));
   }
   const notifications = await encryptCallback(
-    senderDeviceID,
+    encryptedNotifUtilsAPI,
+    senderDeviceDescriptor,
     devices,
     notification,
     codeVersion,
@@ -354,6 +359,7 @@
           },
         };
   return await conditionallyEncryptNotification(
+    encryptedNotifUtilsAPI,
     { keyserverID },
     notification,
     codeVersion,
@@ -383,6 +389,7 @@
     },
   };
   const targetedRescinds = await conditionallyEncryptNotification(
+    encryptedNotifUtilsAPI,
     { keyserverID },
     notification,
     codeVersion,
diff --git a/keyserver/src/push/send.js b/keyserver/src/push/send.js
--- a/keyserver/src/push/send.js
+++ b/keyserver/src/push/send.js
@@ -56,6 +56,7 @@
   prepareEncryptedWebNotifications,
   prepareEncryptedWNSNotifications,
 } from './crypto.js';
+import encryptedNotifUtilsAPI from './encrypted-notif-utils-api.js';
 import { getAPNsNotificationTopic } from './providers.js';
 import { rescindPushNotifs } from './rescind.js';
 import type { TargetedAPNsNotification } from './types.js';
@@ -70,7 +71,6 @@
   wnsMaxNotificationPayloadByteSize,
   wnsPush,
   type WNSPushError,
-  blobServiceUpload,
 } from './utils.js';
 import createIDs from '../creators/id-creator.js';
 import { createUpdates } from '../creators/update-creator.js';
@@ -1032,6 +1032,7 @@
   if (platformDetails.platform === 'macos') {
     const macOSNotifsWithoutMessageInfos =
       await prepareEncryptedAPNsNotifications(
+        encryptedNotifUtilsAPI,
         { keyserverID },
         devices,
         notification,
@@ -1046,6 +1047,7 @@
   }
 
   const notifsWithMessageInfos = await prepareEncryptedAPNsNotifications(
+    encryptedNotifUtilsAPI,
     { keyserverID },
     devices,
     copyWithMessageInfos,
@@ -1083,7 +1085,7 @@
   let blobHash, blobHolders, encryptionKey, blobUploadError;
   if (canQueryBlobService) {
     ({ blobHash, blobHolders, encryptionKey, blobUploadError } =
-      await blobServiceUpload(
+      await encryptedNotifUtilsAPI.uploadLargeNotifPayload(
         copyWithMessageInfos.compile(),
         devicesWithExcessiveSizeNoHolders.length,
       ));
@@ -1116,6 +1118,7 @@
   }
 
   const notifsWithoutMessageInfos = await prepareEncryptedAPNsNotifications(
+    encryptedNotifUtilsAPI,
     { keyserverID },
     devicesWithExcessiveSize,
     notification,
@@ -1241,8 +1244,9 @@
   const priority = 'high';
   if (!shouldBeEncrypted) {
     const notificationToSend =
-      Buffer.byteLength(JSON.stringify(copyWithMessageInfos)) <=
-      fcmMaxNotificationPayloadByteSize
+      encryptedNotifUtilsAPI.getNotifByteSize(
+        JSON.stringify(copyWithMessageInfos),
+      ) <= fcmMaxNotificationPayloadByteSize
         ? copyWithMessageInfos
         : notification;
 
@@ -1257,12 +1261,14 @@
     const serializedNotif = JSON.stringify(notif);
     return (
       !serializedNotif ||
-      Buffer.byteLength(serializedNotif) <= fcmMaxNotificationPayloadByteSize
+      encryptedNotifUtilsAPI.getNotifByteSize(serializedNotif) <=
+        fcmMaxNotificationPayloadByteSize
     );
   };
 
   const notifsWithMessageInfos =
     await prepareEncryptedAndroidVisualNotifications(
+      encryptedNotifUtilsAPI,
       { keyserverID },
       devices,
       copyWithMessageInfos,
@@ -1291,7 +1297,7 @@
   let blobHash, blobHolders, encryptionKey, blobUploadError;
   if (canQueryBlobService) {
     ({ blobHash, blobHolders, encryptionKey, blobUploadError } =
-      await blobServiceUpload(
+      await encryptedNotifUtilsAPI.uploadLargeNotifPayload(
         JSON.stringify(copyWithMessageInfos.data),
         devicesWithExcessiveSizeNoHolders.length,
       ));
@@ -1325,6 +1331,7 @@
 
   const notifsWithoutMessageInfos =
     await prepareEncryptedAndroidVisualNotifications(
+      encryptedNotifUtilsAPI,
       { keyserverID },
       devicesWithExcessiveSize,
       notification,
@@ -1396,6 +1403,7 @@
   }
 
   return prepareEncryptedWebNotifications(
+    encryptedNotifUtilsAPI,
     { keyserverID },
     devices,
     notification,
@@ -1434,7 +1442,7 @@
   };
 
   if (
-    Buffer.byteLength(JSON.stringify(notification)) >
+    encryptedNotifUtilsAPI.getNotifByteSize(JSON.stringify(notification)) >
     wnsMaxNotificationPayloadByteSize
   ) {
     console.warn('WNS notification exceeds size limit');
@@ -1451,6 +1459,7 @@
     }));
   }
   return await prepareEncryptedWNSNotifications(
+    encryptedNotifUtilsAPI,
     { keyserverID },
     devices,
     notification,
@@ -1807,6 +1816,7 @@
         let targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>;
         if (codeVersion > 222) {
           const notificationsArray = await prepareEncryptedAPNsNotifications(
+            encryptedNotifUtilsAPI,
             { keyserverID },
             deviceInfos,
             notification,
@@ -1859,6 +1869,7 @@
         if (codeVersion > 222) {
           const notificationsArray =
             await prepareEncryptedAndroidSilentNotifications(
+              encryptedNotifUtilsAPI,
               { keyserverID },
               deviceInfos,
               notification,
@@ -1917,6 +1928,7 @@
         let targetedNotifications: $ReadOnlyArray<TargetedAPNsNotification>;
         if (shouldBeEncrypted) {
           const notificationsArray = await prepareEncryptedAPNsNotifications(
+            encryptedNotifUtilsAPI,
             { keyserverID },
             deviceInfos,
             notification,
diff --git a/lib/types/notif-types.js b/lib/types/notif-types.js
--- a/lib/types/notif-types.js
+++ b/lib/types/notif-types.js
@@ -1,5 +1,6 @@
 // @flow
 
+import type { EncryptResult } from '@commapp/olm';
 import t, { type TInterface } from 'tcomb';
 
 import type { EntityText, ThreadEntity } from '../utils/entity-text.js';
@@ -185,3 +186,27 @@
   +deviceToken: string,
   +blobHolder?: string,
 };
+
+export type EncryptedNotifUtilsAPI = {
+  +encryptSerializedNotifPayload: (
+    cryptoID: string,
+    unencryptedPayload: string,
+    encryptedPayloadSizeValidator?: (encryptedPayload: string) => boolean,
+  ) => Promise<{
+    +encryptedData: EncryptResult,
+    +sizeLimitViolated?: boolean,
+    +encryptionOrder?: number,
+  }>,
+  +uploadLargeNotifPayload: (
+    payload: string,
+    numberOfHolders: number,
+  ) => Promise<
+    | {
+        +blobHolders: $ReadOnlyArray<string>,
+        +blobHash: string,
+        +encryptionKey: string,
+      }
+    | { +blobUploadError: string },
+  >,
+  +getNotifByteSize: (serializedNotification: string) => number,
+};