Improvements to shared library components
This commit is contained in:
parent
2c59c71872
commit
d9e90e9ea8
15 changed files with 485 additions and 348 deletions
269
ts/util/getProfile.ts
Normal file
269
ts/util/getProfile.ts
Normal file
|
@ -0,0 +1,269 @@
|
|||
// Copyright 2020-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { ProfileKeyCredentialRequestContext } from 'zkgroup';
|
||||
import { SEALED_SENDER } from '../types/SealedSender';
|
||||
import {
|
||||
base64ToArrayBuffer,
|
||||
stringFromBytes,
|
||||
trimForDisplay,
|
||||
verifyAccessKey,
|
||||
} from '../Crypto';
|
||||
import {
|
||||
generateProfileKeyCredentialRequest,
|
||||
getClientZkProfileOperations,
|
||||
handleProfileKeyCredential,
|
||||
} from './zkgroup';
|
||||
import { getSendOptions } from './getSendOptions';
|
||||
import { isMe } from './whatTypeOfConversation';
|
||||
|
||||
export async function 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 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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue