Improvements to shared library components

This commit is contained in:
Josh Perez 2021-07-21 16:45:41 -04:00 committed by GitHub
parent 2c59c71872
commit d9e90e9ea8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 485 additions and 348 deletions

View file

@ -3,7 +3,6 @@
/* eslint-disable class-methods-use-this */
/* eslint-disable camelcase */
import { ProfileKeyCredentialRequestContext } from 'zkgroup';
import { compact, sample } from 'lodash';
import {
ConversationAttributesType,
@ -39,8 +38,6 @@ import {
deriveAccessKey,
fromEncodedBinaryToArrayBuffer,
stringFromBytes,
trimForDisplay,
verifyAccessKey,
} from '../Crypto';
import * as Bytes from '../Bytes';
import { BodyRangesType } from '../types/Util';
@ -82,6 +79,8 @@ import {
import { Deletes } from '../messageModifiers/Deletes';
import { Reactions, ReactionModel } from '../messageModifiers/Reactions';
import { isAnnouncementGroupReady } from '../util/isAnnouncementGroupReady';
import { getProfile } from '../util/getProfile';
import { SEALED_SENDER } from '../types/SealedSender';
// TODO: remove once we move away from ArrayBuffers
const FIXMEU8 = Uint8Array;
@ -89,13 +88,6 @@ const FIXMEU8 = Uint8Array;
/* eslint-disable more/no-then */
window.Whisper = window.Whisper || {};
const SEALED_SENDER = {
UNKNOWN: 0,
ENABLED: 1,
DISABLED: 2,
UNRESTRICTED: 3,
};
const { Services, Util } = window.Signal;
const { Contact, Message } = window.Signal.Types;
const {
@ -4388,273 +4380,11 @@ export class ConversationModel extends window.Backbone
const conversations = (this.getMembers() as unknown) as Array<ConversationModel>;
return Promise.all(
window._.map(conversations, conversation => {
this.getProfile(conversation.get('uuid'), conversation.get('e164'));
getProfile(conversation.get('uuid'), conversation.get('e164'));
})
);
}
async getProfile(
providedUuid?: string,
providedE164?: string
): Promise<void> {
if (!window.textsecure.messaging) {
throw new Error(
'Conversation.getProfile: window.textsecure.messaging not available'
);
}
const id = window.ConversationController.ensureContactIds({
uuid: providedUuid,
e164: providedE164,
});
const c = window.ConversationController.get(id);
if (!c) {
window.log.error(
'getProfile: failed to find conversation; doing nothing'
);
return;
}
const {
generateProfileKeyCredentialRequest,
getClientZkProfileOperations,
handleProfileKeyCredential,
} = Util.zkgroup;
const clientZkProfileCipher = getClientZkProfileOperations(
window.getServerPublicParams()
);
let profile;
try {
await Promise.all([
c.deriveAccessKeyIfNeeded(),
c.deriveProfileKeyVersionIfNeeded(),
]);
const profileKey = c.get('profileKey');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const uuid = c.get('uuid')!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const identifier = c.getSendTarget()!;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const profileKeyVersionHex = c.get('profileKeyVersion')!;
const existingProfileKeyCredential = c.get('profileKeyCredential');
let profileKeyCredentialRequestHex: undefined | string;
let profileCredentialRequestContext:
| undefined
| ProfileKeyCredentialRequestContext;
if (
profileKey &&
uuid &&
profileKeyVersionHex &&
!existingProfileKeyCredential
) {
window.log.info('Generating request...');
({
requestHex: profileKeyCredentialRequestHex,
context: profileCredentialRequestContext,
} = generateProfileKeyCredentialRequest(
clientZkProfileCipher,
uuid,
profileKey
));
}
const { sendMetadata = {} } = await getSendOptions(c.attributes);
const getInfo =
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
sendMetadata[c.get('uuid')!] || sendMetadata[c.get('e164')!] || {};
if (getInfo.accessKey) {
try {
profile = await window.textsecure.messaging.getProfile(identifier, {
accessKey: getInfo.accessKey,
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
} catch (error) {
if (error.code === 401 || error.code === 403) {
window.log.info(
`Setting sealedSender to DISABLED for conversation ${c.idForLogging()}`
);
c.set({ sealedSender: SEALED_SENDER.DISABLED });
profile = await window.textsecure.messaging.getProfile(identifier, {
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
} else {
throw error;
}
}
} else {
profile = await window.textsecure.messaging.getProfile(identifier, {
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
}
const identityKey = base64ToArrayBuffer(profile.identityKey);
const changed = await window.textsecure.storage.protocol.saveIdentity(
`${identifier}.1`,
identityKey,
false
);
if (changed) {
// save identity will close all sessions except for .1, so we
// must close that one manually.
await window.textsecure.storage.protocol.archiveSession(
`${identifier}.1`
);
}
const accessKey = c.get('accessKey');
if (
profile.unrestrictedUnidentifiedAccess &&
profile.unidentifiedAccess
) {
window.log.info(
`Setting sealedSender to UNRESTRICTED for conversation ${c.idForLogging()}`
);
c.set({
sealedSender: SEALED_SENDER.UNRESTRICTED,
});
} else if (accessKey && profile.unidentifiedAccess) {
const haveCorrectKey = await verifyAccessKey(
base64ToArrayBuffer(accessKey),
base64ToArrayBuffer(profile.unidentifiedAccess)
);
if (haveCorrectKey) {
window.log.info(
`Setting sealedSender to ENABLED for conversation ${c.idForLogging()}`
);
c.set({
sealedSender: SEALED_SENDER.ENABLED,
});
} else {
window.log.info(
`Setting sealedSender to DISABLED for conversation ${c.idForLogging()}`
);
c.set({
sealedSender: SEALED_SENDER.DISABLED,
});
}
} else {
window.log.info(
`Setting sealedSender to DISABLED for conversation ${c.idForLogging()}`
);
c.set({
sealedSender: SEALED_SENDER.DISABLED,
});
}
if (profile.about) {
const key = c.get('profileKey');
if (key) {
const keyBuffer = base64ToArrayBuffer(key);
const decrypted = await window.textsecure.crypto.decryptProfile(
base64ToArrayBuffer(profile.about),
keyBuffer
);
c.set('about', stringFromBytes(trimForDisplay(decrypted)));
}
} else {
c.unset('about');
}
if (profile.aboutEmoji) {
const key = c.get('profileKey');
if (key) {
const keyBuffer = base64ToArrayBuffer(key);
const decrypted = await window.textsecure.crypto.decryptProfile(
base64ToArrayBuffer(profile.aboutEmoji),
keyBuffer
);
c.set('aboutEmoji', stringFromBytes(trimForDisplay(decrypted)));
}
} else {
c.unset('aboutEmoji');
}
if (profile.paymentAddress && isMe(c.attributes)) {
window.storage.put('paymentAddress', profile.paymentAddress);
}
if (profile.capabilities) {
c.set({ capabilities: profile.capabilities });
} else {
c.unset('capabilities');
}
if (profileCredentialRequestContext) {
if (profile.credential) {
const profileKeyCredential = handleProfileKeyCredential(
clientZkProfileCipher,
profileCredentialRequestContext,
profile.credential
);
c.set({ profileKeyCredential });
} else {
c.unset('profileKeyCredential');
}
}
} catch (error) {
switch (error?.code) {
case 403:
throw error;
case 404:
window.log.warn(
`getProfile failure: failed to find a profile for ${c.idForLogging()}`,
error && error.stack ? error.stack : error
);
c.setUnregistered();
return;
default:
window.log.warn(
'getProfile failure:',
c.idForLogging(),
error && error.stack ? error.stack : error
);
return;
}
}
try {
await c.setEncryptedProfileName(profile.name);
} catch (error) {
window.log.warn(
'getProfile decryption failure:',
c.idForLogging(),
error && error.stack ? error.stack : error
);
await c.set({
profileName: undefined,
profileFamilyName: undefined,
});
}
try {
await c.setProfileAvatar(profile.avatar);
} catch (error) {
if (error.code === 403 || error.code === 404) {
window.log.info(
`Clearing profile avatar for conversation ${c.idForLogging()}`
);
c.set({
profileAvatar: null,
});
}
}
c.set('profileLastFetchedAt', Date.now());
window.Signal.Data.updateConversation(c.attributes);
}
async setEncryptedProfileName(encryptedName: string): Promise<void> {
if (!encryptedName) {
return;