mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
make sure profileKey is a hex string in all convos
This commit is contained in:
parent
9f62d6577c
commit
fce86989f0
9 changed files with 69 additions and 138 deletions
|
@ -55,60 +55,6 @@ function buildAvatarUpdater({ field }) {
|
|||
}
|
||||
|
||||
const maybeUpdateAvatar = buildAvatarUpdater({ field: 'avatar' });
|
||||
const maybeUpdateProfileAvatar = buildAvatarUpdater({
|
||||
field: 'profileAvatar',
|
||||
});
|
||||
|
||||
async function upgradeToVersion2(conversation, options) {
|
||||
if (conversation.version >= 2) {
|
||||
return conversation;
|
||||
}
|
||||
|
||||
const { writeNewAttachmentData } = options;
|
||||
if (!isFunction(writeNewAttachmentData)) {
|
||||
throw new Error('Conversation.upgradeToVersion2: writeNewAttachmentData must be a function');
|
||||
}
|
||||
|
||||
let { avatar, profileAvatar, profileKey } = conversation;
|
||||
|
||||
if (avatar && avatar.data) {
|
||||
avatar = {
|
||||
hash: await computeHash(avatar.data),
|
||||
path: await writeNewAttachmentData(avatar.data),
|
||||
};
|
||||
}
|
||||
|
||||
if (profileAvatar && profileAvatar.data) {
|
||||
profileAvatar = {
|
||||
hash: await computeHash(profileAvatar.data),
|
||||
path: await writeNewAttachmentData(profileAvatar.data),
|
||||
};
|
||||
}
|
||||
|
||||
if (profileKey && profileKey.byteLength) {
|
||||
profileKey = arrayBufferToBase64(profileKey);
|
||||
}
|
||||
|
||||
return {
|
||||
...conversation,
|
||||
version: 2,
|
||||
avatar,
|
||||
profileAvatar,
|
||||
profileKey,
|
||||
};
|
||||
}
|
||||
|
||||
async function migrateConversation(conversation, options = {}) {
|
||||
if (!conversation) {
|
||||
return conversation;
|
||||
}
|
||||
if (!isNumber(conversation.version)) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
conversation.version = 1;
|
||||
}
|
||||
|
||||
return upgradeToVersion2(conversation, options);
|
||||
}
|
||||
|
||||
async function deleteExternalFiles(conversation, options = {}) {
|
||||
if (!conversation) {
|
||||
|
@ -133,9 +79,7 @@ async function deleteExternalFiles(conversation, options = {}) {
|
|||
|
||||
module.exports = {
|
||||
deleteExternalFiles,
|
||||
migrateConversation,
|
||||
maybeUpdateAvatar,
|
||||
maybeUpdateProfileAvatar,
|
||||
createLastMessageUpdate,
|
||||
arrayBufferToBase64,
|
||||
};
|
||||
|
|
|
@ -41,7 +41,7 @@ import {
|
|||
import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsManager';
|
||||
import { IMAGE_JPEG } from '../types/MIME';
|
||||
import { FSv2 } from '../fileserver';
|
||||
import { fromBase64ToArray, toHex } from '../session/utils/String';
|
||||
import { fromBase64ToArray, fromHexToArray, toHex } from '../session/utils/String';
|
||||
import { SessionButtonColor } from '../components/session/SessionButton';
|
||||
import { perfEnd, perfStart } from '../session/utils/Performance';
|
||||
import { ReplyingToMessageProps } from '../components/session/conversation/SessionCompositionBox';
|
||||
|
@ -350,17 +350,21 @@ export async function uploadOurAvatar(newAvatarDecrypted?: ArrayBuffer) {
|
|||
return;
|
||||
}
|
||||
|
||||
let profileKey;
|
||||
let profileKey: Uint8Array | null;
|
||||
let decryptedAvatarData;
|
||||
if (newAvatarDecrypted) {
|
||||
// Encrypt with a new key every time
|
||||
profileKey = window.libsignal.crypto.getRandomBytes(32);
|
||||
profileKey = window.libsignal.crypto.getRandomBytes(32) as Uint8Array;
|
||||
decryptedAvatarData = newAvatarDecrypted;
|
||||
} else {
|
||||
// this is a reupload. no need to generate a new profileKey
|
||||
profileKey = window.textsecure.storage.get('profileKey');
|
||||
const ourConvoProfileKey =
|
||||
getConversationController()
|
||||
.get(UserUtils.getOurPubKeyStrFromCache())
|
||||
?.get('profileKey') || null;
|
||||
profileKey = ourConvoProfileKey ? fromHexToArray(ourConvoProfileKey) : null;
|
||||
if (!profileKey) {
|
||||
window.log.info('our profileKey not found');
|
||||
window.log.info('our profileKey not found. Not reuploading our avatar');
|
||||
return;
|
||||
}
|
||||
const currentAttachmentPath = ourConvo.getAvatarPath();
|
||||
|
@ -412,7 +416,6 @@ export async function uploadOurAvatar(newAvatarDecrypted?: ArrayBuffer) {
|
|||
const displayName = ourConvo.get('profileName');
|
||||
|
||||
// write the profileKey even if it did not change
|
||||
window.storage.put('profileKey', profileKey);
|
||||
ourConvo.set({ profileKey: toHex(profileKey) });
|
||||
// Replace our temporary image with the attachment pointer from the server:
|
||||
// this commits already
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
saveMessages,
|
||||
updateConversation,
|
||||
} from '../../ts/data/data';
|
||||
import { fromArrayBufferToBase64, fromBase64ToArrayBuffer } from '../session/utils/String';
|
||||
import { fromArrayBufferToBase64, fromBase64ToArrayBuffer, toHex } from '../session/utils/String';
|
||||
import {
|
||||
actions as conversationActions,
|
||||
conversationChanged,
|
||||
|
@ -91,6 +91,9 @@ export interface ConversationAttributes {
|
|||
nickname?: string;
|
||||
profile?: any;
|
||||
profileAvatar?: any;
|
||||
/**
|
||||
* Consider this being a hex string if it set
|
||||
*/
|
||||
profileKey?: string;
|
||||
triggerNotificationsFor: ConversationNotificationSettingType;
|
||||
isTrustedForAttachmentDownload: boolean;
|
||||
|
@ -128,6 +131,9 @@ export interface ConversationAttributesOptionals {
|
|||
nickname?: string;
|
||||
profile?: any;
|
||||
profileAvatar?: any;
|
||||
/**
|
||||
* Consider this being a hex string if it set
|
||||
*/
|
||||
profileKey?: string;
|
||||
triggerNotificationsFor?: ConversationNotificationSettingType;
|
||||
isTrustedForAttachmentDownload?: boolean;
|
||||
|
@ -1194,15 +1200,28 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
await this.commit();
|
||||
}
|
||||
}
|
||||
public async setProfileKey(profileKey: string) {
|
||||
/**
|
||||
* profileKey MUST be a hex string
|
||||
* @param profileKey MUST be a hex string
|
||||
*/
|
||||
public async setProfileKey(profileKey?: Uint8Array, autoCommit = true) {
|
||||
const re = /[0-9A-Fa-f]*/g;
|
||||
|
||||
if (!profileKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const profileKeyHex = toHex(profileKey);
|
||||
|
||||
// profileKey is a string so we can compare it directly
|
||||
if (this.get('profileKey') !== profileKey) {
|
||||
if (this.get('profileKey') !== profileKeyHex) {
|
||||
this.set({
|
||||
profileKey,
|
||||
profileKey: profileKeyHex,
|
||||
});
|
||||
|
||||
|
||||
await this.commit();
|
||||
if (autoCommit) {
|
||||
await this.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,11 +34,6 @@ async function handleOurProfileUpdate(
|
|||
return;
|
||||
}
|
||||
|
||||
if (profileKey?.length) {
|
||||
window?.log?.info('Saving our profileKey from configuration message');
|
||||
// TODO not sure why we keep our profileKey in storage AND in our conversaio
|
||||
window.textsecure.storage.put('profileKey', profileKey);
|
||||
}
|
||||
const lokiProfile = {
|
||||
displayName,
|
||||
profilePicture,
|
||||
|
|
|
@ -20,11 +20,12 @@ import {
|
|||
} from '../../ts/data/data';
|
||||
import { ConversationModel, ConversationTypeEnum } from '../models/conversation';
|
||||
import { allowOnlyOneAtATime } from '../session/utils/Promise';
|
||||
import { toHex } from '../session/utils/String';
|
||||
|
||||
export async function updateProfileOneAtATime(
|
||||
conversation: ConversationModel,
|
||||
profile: SignalService.DataMessage.ILokiProfile,
|
||||
profileKey: any
|
||||
profileKey?: Uint8Array | null // was any
|
||||
) {
|
||||
if (!conversation?.id) {
|
||||
window?.log?.warn('Cannot update profile with empty convoid');
|
||||
|
@ -39,13 +40,18 @@ export async function updateProfileOneAtATime(
|
|||
async function updateProfile(
|
||||
conversation: ConversationModel,
|
||||
profile: SignalService.DataMessage.ILokiProfile,
|
||||
profileKey: any
|
||||
profileKey?: Uint8Array | null // was any
|
||||
) {
|
||||
const { dcodeIO, textsecure, Signal } = window;
|
||||
|
||||
// Retain old values unless changed:
|
||||
const newProfile = conversation.get('profile') || {};
|
||||
|
||||
if (!profileKey) {
|
||||
window.log.warn("No need to try to update profile. We don't have a profile key");
|
||||
return;
|
||||
}
|
||||
|
||||
newProfile.displayName = profile.displayName;
|
||||
|
||||
if (profile.profilePicture) {
|
||||
|
@ -79,7 +85,7 @@ async function updateProfile(
|
|||
});
|
||||
// Only update the convo if the download and decrypt is a success
|
||||
conversation.set('avatarPointer', profile.profilePicture);
|
||||
conversation.set('profileKey', profileKey);
|
||||
conversation.set('profileKey', toHex(profileKey));
|
||||
({ path } = upgraded);
|
||||
} catch (e) {
|
||||
window?.log?.error(`Could not decrypt profile image: ${e}`);
|
||||
|
@ -422,18 +428,15 @@ export const isDuplicate = (
|
|||
async function handleProfileUpdate(
|
||||
profileKeyBuffer: Uint8Array,
|
||||
convoId: string,
|
||||
convoType: ConversationTypeEnum,
|
||||
isIncoming: boolean
|
||||
) {
|
||||
const profileKey = StringUtils.decode(profileKeyBuffer, 'base64');
|
||||
|
||||
if (!isIncoming) {
|
||||
// We update our own profileKey if it's different from what we have
|
||||
const ourNumber = UserUtils.getOurPubKeyStrFromCache();
|
||||
const me = getConversationController().getOrCreate(ourNumber, ConversationTypeEnum.PRIVATE);
|
||||
|
||||
// Will do the save for us if needed
|
||||
await me.setProfileKey(profileKey);
|
||||
await me.setProfileKey(profileKeyBuffer);
|
||||
} else {
|
||||
const sender = await getConversationController().getOrCreateAndWait(
|
||||
convoId,
|
||||
|
@ -441,7 +444,7 @@ async function handleProfileUpdate(
|
|||
);
|
||||
|
||||
// Will do the save for us
|
||||
await sender.setProfileKey(profileKey);
|
||||
await sender.setProfileKey(profileKeyBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,7 +585,7 @@ export async function handleMessageEvent(event: MessageEvent): Promise<void> {
|
|||
return;
|
||||
}
|
||||
if (message.profileKey?.length) {
|
||||
await handleProfileUpdate(message.profileKey, conversationId, type, isIncoming);
|
||||
await handleProfileUpdate(message.profileKey, conversationId, isIncoming);
|
||||
}
|
||||
|
||||
const msg = createMessage(data, isIncoming);
|
||||
|
|
|
@ -204,16 +204,14 @@ function handleLinkPreviews(messageBody: string, messagePreview: any, message: M
|
|||
}
|
||||
|
||||
async function processProfileKey(
|
||||
source: string,
|
||||
conversation: ConversationModel,
|
||||
sendingDeviceConversation: ConversationModel,
|
||||
profileKeyBuffer: Uint8Array
|
||||
profileKeyBuffer?: Uint8Array
|
||||
) {
|
||||
const profileKey = StringUtils.decode(profileKeyBuffer, 'base64');
|
||||
if (conversation.isPrivate()) {
|
||||
await conversation.setProfileKey(profileKey);
|
||||
await conversation.setProfileKey(profileKeyBuffer);
|
||||
} else {
|
||||
await sendingDeviceConversation.setProfileKey(profileKey);
|
||||
await sendingDeviceConversation.setProfileKey(profileKeyBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,12 +358,7 @@ async function handleRegularMessage(
|
|||
}
|
||||
|
||||
if (dataMessage.profileKey) {
|
||||
await processProfileKey(
|
||||
source,
|
||||
conversation,
|
||||
sendingDeviceConversation,
|
||||
dataMessage.profileKey
|
||||
);
|
||||
await processProfileKey(conversation, sendingDeviceConversation, dataMessage.profileKey);
|
||||
}
|
||||
|
||||
// we just received a message from that user so we reset the typing indicator for this convo
|
||||
|
|
|
@ -273,42 +273,6 @@ async function handleDecryptedEnvelope(envelope: EnvelopePlus, plaintext: ArrayB
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only used for opengroupv1 it seems.
|
||||
* To be removed soon
|
||||
*/
|
||||
export async function handlePublicMessage(messageData: any) {
|
||||
const { source } = messageData;
|
||||
const { group, profile, profileKey } = messageData.message;
|
||||
|
||||
const isMe = UserUtils.isUsFromCache(source);
|
||||
|
||||
if (!isMe && profile) {
|
||||
const conversation = await getConversationController().getOrCreateAndWait(
|
||||
source,
|
||||
ConversationTypeEnum.PRIVATE
|
||||
);
|
||||
await updateProfileOneAtATime(conversation, profile, profileKey);
|
||||
}
|
||||
|
||||
const isPublicVisibleMessage = group && group.id && !!group.id.match(openGroupPrefixRegex);
|
||||
|
||||
if (!isPublicVisibleMessage) {
|
||||
throw new Error('handlePublicMessage Should only be called with public message groups');
|
||||
}
|
||||
|
||||
const ev = {
|
||||
// Public chat messages from ourselves should be outgoing
|
||||
type: isMe ? 'sent' : 'message',
|
||||
data: messageData,
|
||||
confirm: () => {
|
||||
/* do nothing */
|
||||
},
|
||||
};
|
||||
|
||||
await handleMessageEvent(ev); // open groups v1
|
||||
}
|
||||
|
||||
export async function handleOpenGroupV2Message(
|
||||
message: OpenGroupMessageV2,
|
||||
roomInfos: OpenGroupRequestCommonType
|
||||
|
|
|
@ -3,7 +3,7 @@ import { UserUtils } from '.';
|
|||
import { getItemById } from '../../../ts/data/data';
|
||||
import { KeyPair } from '../../../libtextsecure/libsignal-protocol';
|
||||
import { PubKey } from '../types';
|
||||
import { toHex } from './String';
|
||||
import { fromHexToArray, toHex } from './String';
|
||||
import { getConversationController } from '../conversations';
|
||||
|
||||
export type HexKeyPair = {
|
||||
|
@ -97,14 +97,15 @@ export function getOurProfile(): OurLokiProfile | undefined {
|
|||
// in their primary device's conversation
|
||||
const ourNumber = window.storage.get('primaryDevicePubKey');
|
||||
const ourConversation = getConversationController().get(ourNumber);
|
||||
const profileKey = new Uint8Array(window.storage.get('profileKey'));
|
||||
const ourProfileKeyHex = ourConversation.get('profileKey');
|
||||
const profileKeyAsBytes = ourProfileKeyHex ? fromHexToArray(ourProfileKeyHex) : null;
|
||||
|
||||
const avatarPointer = ourConversation.get('avatarPointer');
|
||||
const { displayName } = ourConversation.getLokiProfile();
|
||||
return {
|
||||
displayName,
|
||||
avatarPointer,
|
||||
profileKey: profileKey.length ? profileKey : null,
|
||||
profileKey: profileKeyAsBytes?.length ? profileKeyAsBytes : null,
|
||||
};
|
||||
} catch (e) {
|
||||
window?.log?.error(`Failed to get our profile: ${e}`);
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
ConfigurationMessageContact,
|
||||
} from '../messages/outgoing/controlMessage/ConfigurationMessage';
|
||||
import { ConversationModel } from '../../models/conversation';
|
||||
import { fromBase64ToArray } from './String';
|
||||
import { fromBase64ToArray, fromHexToArray } from './String';
|
||||
import { SignalService } from '../../protobuf';
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
|
@ -160,9 +160,14 @@ const getValidContacts = (convos: Array<ConversationModel>) => {
|
|||
|
||||
const contacts = contactsModels.map(c => {
|
||||
try {
|
||||
const profileKeyForContact = c.get('profileKey')
|
||||
? fromBase64ToArray(c.get('profileKey') as string)
|
||||
: undefined;
|
||||
const profileKey = c.get('profileKey');
|
||||
let profileKeyForContact;
|
||||
if (typeof profileKey === 'string') {
|
||||
profileKeyForContact = fromHexToArray(profileKey);
|
||||
} else if (profileKey) {
|
||||
console.warn('AUDRIC migration todo');
|
||||
debugger;
|
||||
}
|
||||
|
||||
return new ConfigurationMessageContact({
|
||||
publicKey: c.id,
|
||||
|
@ -189,8 +194,12 @@ export const getCurrentConfigurationMessage = async (convos: Array<ConversationM
|
|||
if (!ourConvo) {
|
||||
window?.log?.error('Could not find our convo while building a configuration message.');
|
||||
}
|
||||
const profileKeyFromStorage = window.storage.get('profileKey');
|
||||
const profileKey = profileKeyFromStorage ? new Uint8Array(profileKeyFromStorage) : undefined;
|
||||
|
||||
const ourProfileKeyHex =
|
||||
getConversationController()
|
||||
.get(UserUtils.getOurPubKeyStrFromCache())
|
||||
?.get('profileKey') || null;
|
||||
const profileKey = ourProfileKeyHex ? fromHexToArray(ourProfileKeyHex) : undefined;
|
||||
|
||||
const profilePicture = ourConvo?.get('avatarPointer') || undefined;
|
||||
const displayName = ourConvo?.getLokiProfile()?.displayName || undefined;
|
||||
|
|
Loading…
Reference in a new issue