Allow requesting profile without profileKey

This commit is contained in:
Fedor Indutny 2022-03-09 12:23:21 -08:00 committed by GitHub
parent 80e445389f
commit 4a00ea46bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 354 additions and 208 deletions

View file

@ -8,6 +8,7 @@ import PQueue from 'p-queue';
import type {
ConversationAttributesType,
ConversationLastProfileType,
ConversationModelCollectionType,
LastMessageStatus,
MessageAttributesType,
@ -145,6 +146,7 @@ const SEND_REPORTING_THRESHOLD_MS = 25;
const MESSAGE_LOAD_CHUNK_SIZE = 30;
const ATTRIBUTES_THAT_DONT_INVALIDATE_PROPS_CACHE = new Set([
'lastProfile',
'profileLastFetchedAt',
'needsStorageServiceSync',
'storageID',
@ -4575,20 +4577,16 @@ export class ConversationModel extends window.Backbone
return this._activeProfileFetch;
}
async setEncryptedProfileName(encryptedName: string): Promise<void> {
async setEncryptedProfileName(
encryptedName: string,
decryptionKey: Uint8Array
): Promise<void> {
if (!encryptedName) {
return;
}
const key = this.get('profileKey');
if (!key) {
return;
}
// decode
const keyBuffer = Bytes.fromBase64(key);
// decrypt
const { given, family } = decryptProfileName(encryptedName, keyBuffer);
const { given, family } = decryptProfileName(encryptedName, decryptionKey);
// encode
const profileName = given ? Bytes.toString(given) : undefined;
@ -4617,7 +4615,10 @@ export class ConversationModel extends window.Backbone
}
}
async setProfileAvatar(avatarPath: undefined | null | string): Promise<void> {
async setProfileAvatar(
avatarPath: undefined | null | string,
decryptionKey: Uint8Array
): Promise<void> {
if (isMe(this.attributes)) {
if (avatarPath) {
window.storage.put('avatarUrl', avatarPath);
@ -4632,14 +4633,9 @@ export class ConversationModel extends window.Backbone
}
const avatar = await window.textsecure.messaging.getAvatar(avatarPath);
const key = this.get('profileKey');
if (!key) {
return;
}
const keyBuffer = Bytes.fromBase64(key);
// decrypt
const decrypted = decryptProfile(avatar, keyBuffer);
const decrypted = decryptProfile(avatar, decryptionKey);
// update the conversation avatar only if hash differs
if (decrypted) {
@ -4666,10 +4662,6 @@ export class ConversationModel extends window.Backbone
`Setting sealedSender to UNKNOWN for conversation ${this.idForLogging()}`
);
this.set({
about: undefined,
aboutEmoji: undefined,
profileAvatar: undefined,
profileKeyVersion: undefined,
profileKeyCredential: null,
accessKey: null,
sealedSender: SEALED_SENDER.UNKNOWN,
@ -4685,10 +4677,7 @@ export class ConversationModel extends window.Backbone
this.captureChange('profileKey');
}
await Promise.all([
this.deriveAccessKeyIfNeeded(),
this.deriveProfileKeyVersionIfNeeded(),
]);
this.deriveAccessKeyIfNeeded();
// We will update the conversation during storage service sync
if (!viaStorageServiceSync) {
@ -4700,7 +4689,7 @@ export class ConversationModel extends window.Backbone
return false;
}
async deriveAccessKeyIfNeeded(): Promise<void> {
deriveAccessKeyIfNeeded(): void {
const profileKey = this.get('profileKey');
if (!profileKey) {
return;
@ -4715,29 +4704,90 @@ export class ConversationModel extends window.Backbone
this.set({ accessKey });
}
async deriveProfileKeyVersionIfNeeded(): Promise<void> {
deriveProfileKeyVersion(): string | undefined {
const profileKey = this.get('profileKey');
if (!profileKey) {
return;
}
const uuid = this.get('uuid');
if (!uuid || this.get('profileKeyVersion')) {
if (!uuid) {
return;
}
const lastProfile = this.get('lastProfile');
if (lastProfile?.profileKey === profileKey) {
return lastProfile.profileKeyVersion;
}
const profileKeyVersion = Util.zkgroup.deriveProfileKeyVersion(
profileKey,
uuid
);
if (!profileKeyVersion) {
log.warn(
'deriveProfileKeyVersionIfNeeded: Failed to derive profile key version, clearing profile key.'
'deriveProfileKeyVersion: Failed to derive profile key version, ' +
'clearing profile key.'
);
this.setProfileKey(undefined);
return;
}
this.set({ profileKeyVersion });
return profileKeyVersion;
}
async updateLastProfile(
oldValue: ConversationLastProfileType | undefined,
{ profileKey, profileKeyVersion }: ConversationLastProfileType
): Promise<void> {
const lastProfile = this.get('lastProfile');
// Atomic updates only
if (lastProfile !== oldValue) {
return;
}
if (
lastProfile?.profileKey === profileKey &&
lastProfile?.profileKeyVersion === profileKeyVersion
) {
return;
}
log.warn(
'ConversationModel.updateLastProfile: updating for',
this.idForLogging()
);
this.set({ lastProfile: { profileKey, profileKeyVersion } });
await window.Signal.Data.updateConversation(this.attributes);
}
async removeLastProfile(
oldValue: ConversationLastProfileType | undefined
): Promise<void> {
// Atomic updates only
if (this.get('lastProfile') !== oldValue) {
return;
}
log.warn(
'ConversationModel.removeLastProfile: called for',
this.idForLogging()
);
this.set({
lastProfile: undefined,
// We don't have any knowledge of profile anymore. Drop all associated
// data.
about: undefined,
aboutEmoji: undefined,
profileAvatar: undefined,
});
await window.Signal.Data.updateConversation(this.attributes);
}
hasMember(identifier: string): boolean {