diff --git a/web/push-notif/notif-crypto-utils.js b/web/push-notif/notif-crypto-utils.js --- a/web/push-notif/notif-crypto-utils.js +++ b/web/push-notif/notif-crypto-utils.js @@ -999,7 +999,9 @@ // notifications account manipulation -async function getNotifsCryptoAccount(): Promise { +// IMPORTANT: This function creates `olm.Account` instance. +// It is important, to free the memory in places where this function is called. +async function getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT(): Promise { const { values: { [INDEXED_DB_NOTIFS_ACCOUNT_KEY]: encryptedNotifsAccount, @@ -1425,7 +1427,7 @@ migrateLegacyOlmNotificationsSessions, updateNotifsUnreadCountStorage, queryNotifsUnreadCountStorage, - getNotifsCryptoAccount, + getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT, persistEncryptionKey, retrieveEncryptionKey, persistNotifsAccountWithOlmData, diff --git a/web/shared-worker/worker/worker-crypto.js b/web/shared-worker/worker/worker-crypto.js --- a/web/shared-worker/worker/worker-crypto.js +++ b/web/shared-worker/worker/worker-crypto.js @@ -49,7 +49,7 @@ getOlmEncryptionKeyDBLabelForDeviceID, encryptNotification, type NotificationAccountWithPicklingKey, - getNotifsCryptoAccount, + getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT, persistNotifsAccountWithOlmData, } from '../../push-notif/notif-crypto-utils.js'; import { @@ -162,7 +162,8 @@ throw new Error('Crypto account not initialized'); } - const notificationAccountWithPicklingKey = await getNotifsCryptoAccount(); + const notificationAccountWithPicklingKey = + await getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT(); const { notificationAccount, picklingKey, @@ -220,6 +221,8 @@ forceWrite: true, }); + notificationAccount.free(); + return { message, messageType }; } @@ -251,7 +254,7 @@ return undefined; } try { - return await getNotifsCryptoAccount(); + return await getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT(); } catch (e) { return undefined; } @@ -387,7 +390,8 @@ } const { contentAccount } = cryptoStore; - const { notificationAccount } = await getNotifsCryptoAccount(); + const { notificationAccount } = + await getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT(); const identityKeysBlob: IdentityKeysBlob = { notificationIdentityPublicKeys: JSON.parse( @@ -402,6 +406,8 @@ signature: contentAccount.sign(payloadToBeSigned), }; + notificationAccount.free(); + return signedIdentityKeysBlob; } @@ -411,7 +417,7 @@ } const { contentAccount } = cryptoStore; const [notifsCryptoAccount, signedIdentityKeysBlob] = await Promise.all([ - getNotifsCryptoAccount(), + getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT(), getSignedIdentityKeysBlob(), ]); @@ -425,6 +431,8 @@ await persistCryptoStore(notifsCryptoAccount); + notifsCryptoAccount.notificationAccount.free(); + return { keyPayload: signedIdentityKeysBlob.payload, keyPayloadSignature: signedIdentityKeysBlob.signature, @@ -506,10 +514,13 @@ } const { contentAccount } = cryptoStore; const [{ notificationAccount }, { payload, signature }] = await Promise.all( - [getNotifsCryptoAccount(), getSignedIdentityKeysBlob()], + [ + getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT(), + getSignedIdentityKeysBlob(), + ], ); - return { + const result = { primaryIdentityPublicKeys: JSON.parse(contentAccount.identity_keys()), notificationIdentityPublicKeys: JSON.parse( notificationAccount.identity_keys(), @@ -517,6 +528,9 @@ blobPayload: payload, signature, }; + + notificationAccount.free(); + return result; }, async encrypt(content: string, deviceID: string): Promise { if (!cryptoStore) { @@ -893,7 +907,8 @@ throw new Error('Crypto account not initialized'); } const { contentAccount } = cryptoStore; - const notifsCryptoAccount = await getNotifsCryptoAccount(); + const notifsCryptoAccount = + await getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT(); const contentOneTimeKeys = getAccountOneTimeKeys( contentAccount, @@ -909,6 +924,8 @@ await persistCryptoStore(notifsCryptoAccount); + notifsCryptoAccount.notificationAccount.free(); + return { contentOneTimeKeys, notificationsOneTimeKeys }; }, async validateAndUploadPrekeys(authMetadata): Promise { @@ -926,7 +943,8 @@ throw new Error('Crypto account not initialized'); } const { contentAccount } = cryptoStore; - const notifsCryptoAccount = await getNotifsCryptoAccount(); + const notifsCryptoAccount = + await getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT(); // Content and notification accounts' keys are always rotated at the same // time so we only need to check one of them. @@ -941,6 +959,7 @@ await persistCryptoStore(notifsCryptoAccount); if (!contentAccount.unpublished_prekey()) { + notifsCryptoAccount.notificationAccount.free(); return; } @@ -963,6 +982,7 @@ notifsCryptoAccount.notificationAccount.mark_prekey_as_published(); await persistCryptoStore(notifsCryptoAccount); + notifsCryptoAccount.notificationAccount.free(); }, async signMessage(message: string): Promise { if (!cryptoStore) { @@ -994,12 +1014,14 @@ throw new Error('Crypto account not initialized'); } const { contentAccount } = cryptoStore; - const notifsCryptoAccount = await getNotifsCryptoAccount(); + const notifsCryptoAccount = + await getNotifsCryptoAccount_WITH_MANUAL_MEMORY_MANAGEMENT(); contentAccount.mark_prekey_as_published(); notifsCryptoAccount.notificationAccount.mark_prekey_as_published(); await persistCryptoStore(notifsCryptoAccount); + notifsCryptoAccount.notificationAccount.free(); }, };