diff --git a/background.html b/background.html index b14e5ee3f..5e70bbd4d 100644 --- a/background.html +++ b/background.html @@ -29,6 +29,7 @@ + diff --git a/package.json b/package.json index 2904f7844..1a45a769a 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "p-retry": "^4.2.0", "pify": "3.0.0", "protobufjs": "^6.11.2", + "queue-promise": "^2.2.1", "rc-slider": "^8.7.1", "react": "^17.0.2", "react-contexify": "5.0.0", diff --git a/ts/components/conversation/media-gallery/LoadingIndicator.tsx b/ts/components/conversation/media-gallery/LoadingIndicator.tsx deleted file mode 100644 index 349560a8e..000000000 --- a/ts/components/conversation/media-gallery/LoadingIndicator.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; - -export const LoadingIndicator = () => { - return ( -
-
- - - -
-
- ); -}; diff --git a/ts/node/sql.ts b/ts/node/sql.ts index c197d9b97..22eda7ed2 100644 --- a/ts/node/sql.ts +++ b/ts/node/sql.ts @@ -139,7 +139,7 @@ function openAndMigrateDatabase(filePath: string, key: string) { // First, we try to open the database without any cipher changes try { - db = new BetterSqlite3.default(filePath, openDbOptions); + db = new (BetterSqlite3 as any).default(filePath, openDbOptions); keyDatabase(db, key); switchToWAL(db); @@ -159,7 +159,7 @@ function openAndMigrateDatabase(filePath: string, key: string) { let db1; try { - db1 = new BetterSqlite3.default(filePath, openDbOptions); + db1 = new (BetterSqlite3 as any).default(filePath, openDbOptions); keyDatabase(db1, key); // https://www.zetetic.net/blog/2018/11/30/sqlcipher-400-release/#compatability-sqlcipher-4-0-0 @@ -177,7 +177,7 @@ function openAndMigrateDatabase(filePath: string, key: string) { // migrate to the latest ciphers after we've modified the defaults. let db2; try { - db2 = new BetterSqlite3.default(filePath, openDbOptions); + db2 = new (BetterSqlite3 as any).default(filePath, openDbOptions); keyDatabase(db2, key); db2.pragma('cipher_migrate'); diff --git a/ts/receiver/configMessage.ts b/ts/receiver/configMessage.ts index e76ccf46d..d7eafadac 100644 --- a/ts/receiver/configMessage.ts +++ b/ts/receiver/configMessage.ts @@ -14,15 +14,14 @@ import { configurationMessageReceived, trigger } from '../shims/events'; import { BlockedNumberController } from '../util'; import { removeFromCache } from './cache'; import { handleNewClosedGroup } from './closedGroups'; -import { updateProfileOneAtATime } from './dataMessage'; import { EnvelopePlus } from './types'; import { ConversationInteraction } from '../interactions'; import { getLastProfileUpdateTimestamp, setLastProfileUpdateTimestamp } from '../util/storage'; +import { appendFetchAvatarAndProfileJob, updateOurProfileSync } from './userProfileImageUpdates'; async function handleOurProfileUpdate( sentAt: number | Long, - configMessage: SignalService.ConfigurationMessage, - ourPubkey: string + configMessage: SignalService.ConfigurationMessage ) { const latestProfileUpdateTimestamp = getLastProfileUpdateTimestamp(); if (!latestProfileUpdateTimestamp || sentAt > latestProfileUpdateTimestamp) { @@ -31,17 +30,11 @@ async function handleOurProfileUpdate( ); const { profileKey, profilePicture, displayName } = configMessage; - const ourConversation = getConversationController().get(ourPubkey); - if (!ourConversation) { - window?.log?.error('We need a convo with ourself at all times'); - return; - } - const lokiProfile = { displayName, profilePicture, }; - await updateProfileOneAtATime(ourConversation, lokiProfile, profileKey); + await updateOurProfileSync(lokiProfile, profileKey); await setLastProfileUpdateTimestamp(_.toNumber(sentAt)); // do not trigger a signin by linking if the display name is empty if (displayName) { @@ -192,7 +185,7 @@ const handleContactFromConfig = async ( await BlockedNumberController.unblock(contactConvo.id); } - void updateProfileOneAtATime(contactConvo, profile, contactReceived.profileKey); + void appendFetchAvatarAndProfileJob(contactConvo, profile, contactReceived.profileKey); } catch (e) { window?.log?.warn('failed to handle a new closed group from configuration message'); } @@ -213,7 +206,7 @@ export async function handleConfigurationMessage( return removeFromCache(envelope); } - await handleOurProfileUpdate(envelope.timestamp, configurationMessage, ourPubkey); + await handleOurProfileUpdate(envelope.timestamp, configurationMessage); await handleGroupsAndContactsFromConfigMessage(envelope, configurationMessage); diff --git a/ts/receiver/dataMessage.ts b/ts/receiver/dataMessage.ts index 97c3734bf..7d22f68a6 100644 --- a/ts/receiver/dataMessage.ts +++ b/ts/receiver/dataMessage.ts @@ -5,7 +5,6 @@ import { getEnvelopeId } from './common'; import { PubKey } from '../session/types'; import { handleMessageJob, toRegularMessage } from './queuedJob'; -import { downloadAttachment } from './attachments'; import _ from 'lodash'; import { StringUtils, UserUtils } from '../session/utils'; import { getConversationController } from '../session/conversations'; @@ -15,104 +14,15 @@ import { getMessageBySenderAndServerTimestamp, } from '../../ts/data/data'; import { ConversationModel, ConversationTypeEnum } from '../models/conversation'; -import { allowOnlyOneAtATime } from '../session/utils/Promise'; -import { toHex } from '../session/utils/String'; import { toLogFormat } from '../types/attachments/Errors'; -import { processNewAttachment } from '../types/MessageAttachment'; -import { MIME } from '../types'; -import { autoScaleForIncomingAvatar } from '../util/attachmentsUtil'; + import { createSwarmMessageSentFromNotUs, createSwarmMessageSentFromUs, } from '../models/messageFactory'; import { MessageModel } from '../models/message'; import { isUsFromCache } from '../session/utils/User'; -import { decryptProfile } from '../util/crypto/profileEncrypter'; -import ByteBuffer from 'bytebuffer'; - -export async function updateProfileOneAtATime( - conversation: ConversationModel, - profile: SignalService.DataMessage.ILokiProfile, - profileKey?: Uint8Array | null // was any -) { - if (!conversation?.id) { - window?.log?.warn('Cannot update profile with empty convoid'); - return; - } - const oneAtaTimeStr = `updateProfileOneAtATime:${conversation.id}`; - return allowOnlyOneAtATime(oneAtaTimeStr, async () => { - return createOrUpdateProfile(conversation, profile, profileKey); - }); -} - -/** - * Creates a new profile from the profile provided. Creates the profile if it doesn't exist. - */ -async function createOrUpdateProfile( - conversation: ConversationModel, - profile: SignalService.DataMessage.ILokiProfile, - profileKey?: Uint8Array | null -) { - // Retain old values unless changed: - const newProfile = conversation.get('profile') || {}; - - newProfile.displayName = profile.displayName; - - if (profile.profilePicture && profileKey) { - const prevPointer = conversation.get('avatarPointer'); - const needsUpdate = !prevPointer || !_.isEqual(prevPointer, profile.profilePicture); - - if (needsUpdate) { - try { - const downloaded = await downloadAttachment({ - url: profile.profilePicture, - isRaw: true, - }); - - // null => use placeholder with color and first letter - let path = null; - if (profileKey) { - // Convert profileKey to ArrayBuffer, if needed - const encoding = typeof profileKey === 'string' ? 'base64' : null; - try { - const profileKeyArrayBuffer = dcodeIO.ByteBuffer.wrap( - profileKey, - encoding - ).toArrayBuffer(); - const decryptedData = await decryptProfile(downloaded.data, profileKeyArrayBuffer); - - const scaledData = await autoScaleForIncomingAvatar(decryptedData); - const upgraded = await processNewAttachment({ - data: await scaledData.blob.arrayBuffer(), - contentType: MIME.IMAGE_UNKNOWN, // contentType is mostly used to generate previews and screenshot. We do not care for those in this case. - }); - // Only update the convo if the download and decrypt is a success - conversation.set('avatarPointer', profile.profilePicture); - conversation.set('profileKey', toHex(profileKey)); - ({ path } = upgraded); - } catch (e) { - window?.log?.error(`Could not decrypt profile image: ${e}`); - } - } - newProfile.avatar = path; - } catch (e) { - window.log.warn( - `Failed to download attachment at ${profile.profilePicture}. Maybe it expired? ${e.message}` - ); - // do not return here, we still want to update the display name even if the avatar failed to download - } - } - } else if (profileKey) { - newProfile.avatar = null; - } - - const conv = await getConversationController().getOrCreateAndWait( - conversation.id, - ConversationTypeEnum.PRIVATE - ); - await conv.setLokiProfile(newProfile); - await conv.commit(); -} +import { appendFetchAvatarAndProfileJob } from './userProfileImageUpdates'; function cleanAttachment(attachment: any) { return { @@ -303,7 +213,7 @@ export async function handleSwarmDataMessage( cleanDataMessage.profileKey?.length ) { // do not await this - void updateProfileOneAtATime( + void appendFetchAvatarAndProfileJob( senderConversationModel, cleanDataMessage.profile, cleanDataMessage.profileKey diff --git a/ts/receiver/queuedJob.ts b/ts/receiver/queuedJob.ts index 3ce440558..7e3625d9e 100644 --- a/ts/receiver/queuedJob.ts +++ b/ts/receiver/queuedJob.ts @@ -8,13 +8,13 @@ import { ConversationModel, ConversationTypeEnum } from '../models/conversation' import { MessageModel } from '../models/message'; import { getMessageById, getMessageCountByType, getMessagesBySentAt } from '../../ts/data/data'; -import { updateProfileOneAtATime } from './dataMessage'; import { SignalService } from '../protobuf'; import { UserUtils } from '../session/utils'; import { showMessageRequestBanner } from '../state/ducks/userConfig'; import { MessageDirection } from '../models/messageType'; import { LinkPreviews } from '../util/linkPreviews'; import { GoogleChrome } from '../util'; +import { appendFetchAvatarAndProfileJob } from './userProfileImageUpdates'; function contentTypeSupported(type: string): boolean { const Chrome = GoogleChrome; @@ -295,7 +295,7 @@ async function handleRegularMessage( // the only profile we don't update with what is coming here is ours, // as our profile is shared accross our devices with a ConfigurationMessage if (type === 'incoming' && rawDataMessage.profile) { - void updateProfileOneAtATime( + void appendFetchAvatarAndProfileJob( sendingDeviceConversation, rawDataMessage.profile, rawDataMessage.profileKey diff --git a/ts/receiver/userProfileImageUpdates.ts b/ts/receiver/userProfileImageUpdates.ts new file mode 100644 index 000000000..fd624d454 --- /dev/null +++ b/ts/receiver/userProfileImageUpdates.ts @@ -0,0 +1,157 @@ +import Queue from 'queue-promise'; +import ByteBuffer from 'bytebuffer'; +import _ from 'lodash'; + +import { downloadAttachment } from './attachments'; + +import { allowOnlyOneAtATime, hasAlreadyOneAtaTimeMatching } from '../session/utils/Promise'; +import { toHex } from '../session/utils/String'; +import { processNewAttachment } from '../types/MessageAttachment'; +import { MIME } from '../types'; +import { autoScaleForIncomingAvatar } from '../util/attachmentsUtil'; +import { decryptProfile } from '../util/crypto/profileEncrypter'; +import { ConversationModel, ConversationTypeEnum } from '../models/conversation'; +import { SignalService } from '../protobuf'; +import { getConversationController } from '../session/conversations'; +import { UserUtils } from '../session/utils'; + +const queue = new Queue({ + concurrent: 1, + interval: 500, +}); + +queue.on('dequeue', () => { + // window.log.info('[profile-update] queue is dequeuing'); +}); +queue.on('resolve', () => { + // window.log.info('[profile-update] task resolved'); +}); +queue.on('reject', error => { + window.log.warn('[profile-update] task profile image update failed with', error); +}); +queue.on('start', () => { + window.log.info('[profile-update] queue is starting'); +}); +queue.on('stop', () => { + window.log.info('[profile-update] queue is stopping'); +}); +queue.on('end', () => { + window.log.info('[profile-update] queue is ending'); +}); + +export async function appendFetchAvatarAndProfileJob( + conversation: ConversationModel, + profile: SignalService.DataMessage.ILokiProfile, + profileKey?: Uint8Array | null // was any +) { + if (!conversation?.id) { + window?.log?.warn('[profile-update] Cannot update profile with empty convoid'); + return; + } + const oneAtaTimeStr = `appendFetchAvatarAndProfileJob:${conversation.id}`; + + if (hasAlreadyOneAtaTimeMatching(oneAtaTimeStr)) { + window.log.info( + '[profile-update] not adding another task of "appendFetchAvatarAndProfileJob" as there is already one scheduled for the conversation: ', + conversation.id + ); + return; + } + window.log.info( + '[profile-update] "appendFetchAvatarAndProfileJob" as there is already one scheduled for the conversation: ', + conversation.id + ); + const task = allowOnlyOneAtATime(oneAtaTimeStr, async () => { + return createOrUpdateProfile(conversation, profile, profileKey); + }); + + queue.enqueue(async () => task); +} + +/** + * This function should be used only when we have to do a sync update to our conversation with a new profile/avatar image or display name + * It tries to fetch the profile image, scale it, save it, and update the conversationModel + */ +export async function updateOurProfileSync( + profile: SignalService.DataMessage.ILokiProfile, + profileKey?: Uint8Array | null // was any +) { + const ourConvo = getConversationController().get(UserUtils.getOurPubKeyStrFromCache()); + if (!ourConvo?.id) { + window?.log?.warn('[profile-update] Cannot update our profile with empty convoid'); + return; + } + const oneAtaTimeStr = `appendFetchAvatarAndProfileJob:${ourConvo.id}`; + return allowOnlyOneAtATime(oneAtaTimeStr, async () => { + return createOrUpdateProfile(ourConvo, profile, profileKey); + }); +} + +/** + * Creates a new profile from the profile provided. Creates the profile if it doesn't exist. + */ +async function createOrUpdateProfile( + conversation: ConversationModel, + profile: SignalService.DataMessage.ILokiProfile, + profileKey?: Uint8Array | null +) { + // Retain old values unless changed: + const newProfile = conversation.get('profile') || {}; + + newProfile.displayName = profile.displayName; + + if (profile.profilePicture && profileKey) { + const prevPointer = conversation.get('avatarPointer'); + const needsUpdate = !prevPointer || !_.isEqual(prevPointer, profile.profilePicture); + + if (needsUpdate) { + try { + const downloaded = await downloadAttachment({ + url: profile.profilePicture, + isRaw: true, + }); + + // null => use placeholder with color and first letter + let path = null; + if (profileKey) { + // Convert profileKey to ArrayBuffer, if needed + const encoding = typeof profileKey === 'string' ? 'base64' : undefined; + try { + const profileKeyArrayBuffer = ByteBuffer.wrap(profileKey, encoding).toArrayBuffer(); + const decryptedData = await decryptProfile(downloaded.data, profileKeyArrayBuffer); + window.log.info( + `[profile-update] about to auto scale avatar for convo ${conversation.id}` + ); + + const scaledData = await autoScaleForIncomingAvatar(decryptedData); + const upgraded = await processNewAttachment({ + data: await scaledData.blob.arrayBuffer(), + contentType: MIME.IMAGE_UNKNOWN, // contentType is mostly used to generate previews and screenshot. We do not care for those in this case. + }); + // Only update the convo if the download and decrypt is a success + conversation.set('avatarPointer', profile.profilePicture); + conversation.set('profileKey', toHex(profileKey)); + ({ path } = upgraded); + } catch (e) { + window?.log?.error(`[profile-update] Could not decrypt profile image: ${e}`); + } + } + newProfile.avatar = path; + } catch (e) { + window.log.warn( + `[profile-update] Failed to download attachment at ${profile.profilePicture}. Maybe it expired? ${e.message}` + ); + // do not return here, we still want to update the display name even if the avatar failed to download + } + } + } else if (profileKey) { + newProfile.avatar = null; + } + + const conv = await getConversationController().getOrCreateAndWait( + conversation.id, + ConversationTypeEnum.PRIVATE + ); + await conv.setLokiProfile(newProfile); + await conv.commit(); +} diff --git a/ts/session/apis/file_server_api/FileServerApiV2.ts b/ts/session/apis/file_server_api/FileServerApiV2.ts index 3496d20ea..16536f610 100644 --- a/ts/session/apis/file_server_api/FileServerApiV2.ts +++ b/ts/session/apis/file_server_api/FileServerApiV2.ts @@ -143,9 +143,8 @@ export const buildUrl = (request: FileServerV2Request | OpenGroupV2Request): URL }; /** - * Upload a file to the file server v2 - * @param fileContent the data to send - * @returns null or the fileID and complete URL to share this file + * Fetch the latest desktop release available on github from the fileserver. + * This call is onion routed and so do not expose our ip to github nor the file server. */ export const getLatestDesktopReleaseFileToFsV2 = async (): Promise => { const queryParams = { diff --git a/ts/session/utils/Promise.ts b/ts/session/utils/Promise.ts index 49d0e765a..3b1bfbb49 100644 --- a/ts/session/utils/Promise.ts +++ b/ts/session/utils/Promise.ts @@ -15,7 +15,7 @@ export class TaskTimedOutError extends Error { } // one action resolves all -const snodeGlobalLocks: Record> = {}; +const oneAtaTimeRecord: Record> = {}; export async function allowOnlyOneAtATime( name: string, @@ -23,16 +23,16 @@ export async function allowOnlyOneAtATime( timeoutMs?: number ) { // if currently not in progress - if (snodeGlobalLocks[name] === undefined) { + if (oneAtaTimeRecord[name] === undefined) { // set lock - snodeGlobalLocks[name] = new Promise(async (resolve, reject) => { + oneAtaTimeRecord[name] = new Promise(async (resolve, reject) => { // set up timeout feature let timeoutTimer = null; if (timeoutMs) { timeoutTimer = setTimeout(() => { - window?.log?.warn(`allowOnlyOneAtATime - TIMEDOUT after ${timeoutMs}s`); + window?.log?.warn(`allowOnlyOneAtATime - TIMEDOUT after ${timeoutMs}ms`); // tslint:disable-next-line: no-dynamic-delete - delete snodeGlobalLocks[name]; // clear lock + delete oneAtaTimeRecord[name]; // clear lock reject(); }, timeoutMs); } @@ -55,7 +55,7 @@ export async function allowOnlyOneAtATime( } } // tslint:disable-next-line: no-dynamic-delete - delete snodeGlobalLocks[name]; // clear lock + delete oneAtaTimeRecord[name]; // clear lock reject(e); } // clear timeout timer @@ -66,12 +66,16 @@ export async function allowOnlyOneAtATime( } } // tslint:disable-next-line: no-dynamic-delete - delete snodeGlobalLocks[name]; // clear lock + delete oneAtaTimeRecord[name]; // clear lock // release the kraken resolve(innerRetVal); }); } - return snodeGlobalLocks[name]; + return oneAtaTimeRecord[name]; +} + +export function hasAlreadyOneAtaTimeMatching(text: string): boolean { + return Boolean(oneAtaTimeRecord[text]); } /** diff --git a/ts/test/session/unit/utils/Promise_test.ts b/ts/test/session/unit/utils/Promise_test.ts index 7035d2b0e..7762dc9a0 100644 --- a/ts/test/session/unit/utils/Promise_test.ts +++ b/ts/test/session/unit/utils/Promise_test.ts @@ -7,6 +7,13 @@ import { PromiseUtils } from '../../../../session/utils'; // tslint:disable-next-line: no-require-imports no-var-requires import chaiAsPromised from 'chai-as-promised'; +import { + allowOnlyOneAtATime, + hasAlreadyOneAtaTimeMatching, + sleepFor, +} from '../../../../session/utils/Promise'; +import { TestUtils } from '../../../test-utils'; + chai.use(chaiAsPromised as any); chai.should(); @@ -34,6 +41,7 @@ describe('Promise Utils', () => { pollSpy = sandbox.spy(PromiseUtils, 'poll'); waitForTaskSpy = sandbox.spy(PromiseUtils, 'waitForTask'); waitUntilSpy = sandbox.spy(PromiseUtils, 'waitUntil'); + TestUtils.stubWindowLog(); }); afterEach(() => { @@ -141,4 +149,68 @@ describe('Promise Utils', () => { return promise.should.eventually.be.rejectedWith('Periodic check timeout'); }); }); + + describe('allowOnlyOneAtATime', () => { + it('start if not running', async () => { + const spy = sinon.spy(async () => { + return sleepFor(10); + }); + await allowOnlyOneAtATime('testing', spy); + expect(spy.callCount).to.be.eq(1); + }); + + it('starts only once if already running', async () => { + const spy = sinon.spy(async () => { + return sleepFor(10); + }); + void allowOnlyOneAtATime('testing', spy); + + await allowOnlyOneAtATime('testing', spy); + expect(spy.callCount).to.be.eq(1); + }); + + it('throw if took longer than expected timeout', async () => { + const spy = sinon.spy(async () => { + return sleepFor(10); + }); + try { + await allowOnlyOneAtATime('testing', spy, 5); + throw new Error('should not get here'); + } catch (e) { + console.warn(e); + expect(e).to.be.be.eql(undefined, 'should be undefined'); + } + + expect(spy.callCount).to.be.eq(1); + }); + + it('does not throw if took less than expected timeout', async () => { + const spy = sinon.spy(async () => { + return sleepFor(10); + }); + try { + await allowOnlyOneAtATime('testing', spy, 15); + throw new Error('should get here'); + } catch (e) { + console.warn(e); + expect(e.message).to.be.be.eql('should get here'); + } + + expect(spy.callCount).to.be.eq(1); + }); + }); + + describe('hasAlreadyOneAtaTimeMatching', () => { + it('returns true if already started', () => { + const spy = sinon.spy(async () => { + return sleepFor(10); + }); + void allowOnlyOneAtATime('testing', spy); + expect(hasAlreadyOneAtaTimeMatching('testing')).to.be.eq(true, 'should be true'); + }); + + it('returns false if not already started', () => { + expect(hasAlreadyOneAtaTimeMatching('testing2')).to.be.eq(false, 'should be false'); + }); + }); }); diff --git a/ts/types/attachments/migrations.ts b/ts/types/attachments/migrations.ts index c533754ba..95407a551 100644 --- a/ts/types/attachments/migrations.ts +++ b/ts/types/attachments/migrations.ts @@ -20,6 +20,7 @@ import { readAttachmentData, writeNewAttachmentData, } from '../MessageAttachment'; +import { perfEnd, perfStart } from '../../session/utils/Performance'; const DEFAULT_JPEG_QUALITY = 0.85; @@ -30,10 +31,13 @@ const DEFAULT_JPEG_QUALITY = 0.85; export const autoOrientJpegImage = async ( fileOrBlobOrURL: string | File | Blob ): Promise => { + perfStart(`autoOrientJpegImage`); const loadedImage = await loadImage(fileOrBlobOrURL, { orientation: true, canvas: true }); - - const canvas = loadedImage.image as HTMLCanvasElement; - const dataURL = canvas.toDataURL(MIME.IMAGE_JPEG, DEFAULT_JPEG_QUALITY); + perfEnd(`autoOrientJpegImage`, `autoOrientJpegImage`); + const dataURL = (loadedImage.image as HTMLCanvasElement).toDataURL( + MIME.IMAGE_JPEG, + DEFAULT_JPEG_QUALITY + ); return dataURL; }; diff --git a/ts/util/attachmentsUtil.ts b/ts/util/attachmentsUtil.ts index e23567c1c..270c47044 100644 --- a/ts/util/attachmentsUtil.ts +++ b/ts/util/attachmentsUtil.ts @@ -11,6 +11,7 @@ import { THUMBNAIL_SIDE } from '../types/attachments/VisualAttachment'; import imageType from 'image-type'; import { MAX_ATTACHMENT_FILESIZE_BYTES } from '../session/constants'; +import { perfEnd, perfStart } from '../session/utils/Performance'; /** * The logic for sending attachments is as follow: @@ -64,6 +65,7 @@ export async function autoScaleForAvatar( canvas: true, }; + perfStart(`loadimage-*${blob.size}`); const canvas = await loadImage(blob, loadImgOpts); - + perfEnd(`loadimage-*${blob.size}`, `loadimage-*${blob.size}`); if (!canvas || !canvas.originalWidth || !canvas.originalHeight) { throw new Error('failed to scale image'); } diff --git a/ts/webworker/workers/util.worker.ts b/ts/webworker/workers/util.worker.ts index a0632dfd5..4c80b7e85 100644 --- a/ts/webworker/workers/util.worker.ts +++ b/ts/webworker/workers/util.worker.ts @@ -167,15 +167,6 @@ async function deriveSymmetricKey(x25519PublicKey: Uint8Array, x25519PrivateKey: return symmetricKey; } -async function generateEphemeralKeyPair() { - const ran = (await getSodiumWorker()).randombytes_buf(32); - const keys = generateKeyPair(ran); - return keys; - // Signal protocol prepends with "0x05" - // keys.pubKey = keys.pubKey.slice(1); - // return { pubKey: keys.public, privKey: keys.private }; -} - function assertArrayBufferView(val: any) { if (!ArrayBuffer.isView(val)) { throw new Error('val type not correct'); @@ -189,7 +180,11 @@ async function encryptForPubkey(pubkeyX25519str: string, payloadBytes: Uint8Arra throw new Error('pubkeyX25519str type not correct'); } assertArrayBufferView(payloadBytes); - const ephemeral = await generateEphemeralKeyPair(); + const ran = (await getSodiumWorker()).randombytes_buf(32); + const ephemeral = generateKeyPair(ran); + // Signal protocol prepends with "0x05" + // keys.pubKey = keys.pubKey.slice(1); + // return { pubKey: keys.public, privKey: keys.private }; const pubkeyX25519Buffer = fromHexToArray(pubkeyX25519str); const symmetricKey = await deriveSymmetricKey( pubkeyX25519Buffer, diff --git a/yarn.lock b/yarn.lock index 482594d42..cc8721f38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6080,6 +6080,11 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +queue-promise@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/queue-promise/-/queue-promise-2.2.1.tgz#8de03fb79ba458efcb5ebf76368ab029d2669752" + integrity sha512-C3eyRwLF9m6dPV4MtqMVFX+Xmc7keZ9Ievm3jJ/wWM5t3uVbFnGsJXwpYzZ4LaIEcX9bss/mdaKzyrO6xheRuA== + quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"