Further PNP fixes

This commit is contained in:
Fedor Indutny 2024-02-16 12:39:58 -08:00 committed by GitHub
parent 16dcf31906
commit dd5b66039d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 71 additions and 50 deletions

View file

@ -201,7 +201,7 @@
"@electron/notarize": "2.1.0", "@electron/notarize": "2.1.0",
"@formatjs/intl": "2.6.7", "@formatjs/intl": "2.6.7",
"@mixer/parallel-prettier": "2.0.3", "@mixer/parallel-prettier": "2.0.3",
"@signalapp/mock-server": "5.0.0", "@signalapp/mock-server": "5.0.1",
"@storybook/addon-a11y": "7.4.5", "@storybook/addon-a11y": "7.4.5",
"@storybook/addon-actions": "7.4.5", "@storybook/addon-actions": "7.4.5",
"@storybook/addon-controls": "7.4.5", "@storybook/addon-controls": "7.4.5",

View file

@ -102,7 +102,6 @@ function applyChangeToConversation(
export type CombineConversationsParams = Readonly<{ export type CombineConversationsParams = Readonly<{
current: ConversationModel; current: ConversationModel;
fromPniSignature?: boolean;
obsolete: ConversationModel; obsolete: ConversationModel;
obsoleteTitleInfo?: ConversationRenderInfoType; obsoleteTitleInfo?: ConversationRenderInfoType;
}>; }>;
@ -495,6 +494,9 @@ export class ConversationController {
if (providedPni) { if (providedPni) {
dataProvided.push(`pni=${providedPni}`); dataProvided.push(`pni=${providedPni}`);
} }
if (fromPniSignature) {
dataProvided.push(`fromPniSignature=${fromPniSignature}`);
}
const logId = `maybeMergeContacts/${reason}/${dataProvided.join(',')}`; const logId = `maybeMergeContacts/${reason}/${dataProvided.join(',')}`;
const aci = providedAci const aci = providedAci
@ -677,7 +679,6 @@ export class ConversationController {
mergePromises.push( mergePromises.push(
mergeOldAndNew({ mergeOldAndNew({
current: targetConversation, current: targetConversation,
fromPniSignature,
logId, logId,
obsolete: match, obsolete: match,
obsoleteTitleInfo, obsoleteTitleInfo,
@ -1021,7 +1022,6 @@ export class ConversationController {
current, current,
obsolete, obsolete,
obsoleteTitleInfo, obsoleteTitleInfo,
fromPniSignature,
}: CombineConversationsParams): Promise<void> { }: CombineConversationsParams): Promise<void> {
const logId = `combineConversations/${obsolete.id}->${current.id}`; const logId = `combineConversations/${obsolete.id}->${current.id}`;
@ -1047,12 +1047,13 @@ export class ConversationController {
const obsoleteActiveAt = obsolete.get('active_at'); const obsoleteActiveAt = obsolete.get('active_at');
const currentActiveAt = current.get('active_at'); const currentActiveAt = current.get('active_at');
const activeAt = let activeAt: number | null | undefined;
!obsoleteActiveAt ||
!currentActiveAt || if (obsoleteActiveAt && currentActiveAt) {
currentActiveAt > obsoleteActiveAt activeAt = Math.max(obsoleteActiveAt, currentActiveAt);
? currentActiveAt } else {
: obsoleteActiveAt; activeAt = obsoleteActiveAt || currentActiveAt;
}
current.set('active_at', activeAt); current.set('active_at', activeAt);
const dataToCopy: Partial<ConversationAttributesType> = pick( const dataToCopy: Partial<ConversationAttributesType> = pick(
@ -1195,12 +1196,7 @@ export class ConversationController {
const titleIsUseful = Boolean( const titleIsUseful = Boolean(
obsoleteTitleInfo && getTitleNoDefault(obsoleteTitleInfo) obsoleteTitleInfo && getTitleNoDefault(obsoleteTitleInfo)
); );
if ( if (obsoleteTitleInfo && titleIsUseful && obsoleteHadMessages) {
obsoleteTitleInfo &&
titleIsUseful &&
!fromPniSignature &&
obsoleteHadMessages
) {
drop(current.addConversationMerge(obsoleteTitleInfo)); drop(current.addConversationMerge(obsoleteTitleInfo));
} }

View file

@ -2635,7 +2635,7 @@ export class SignalProtocolStore extends EventEmitter {
this.ourRegistrationIds.set(pni, registrationId); this.ourRegistrationIds.set(pni, registrationId);
// Update database // Update database
await Promise.all([ await Promise.all<void>([
storage.put('identityKeyMap', { storage.put('identityKeyMap', {
...(storage.get('identityKeyMap') || {}), ...(storage.get('identityKeyMap') || {}),
[pni]: { [pni]: {
@ -2647,11 +2647,11 @@ export class SignalProtocolStore extends EventEmitter {
...(storage.get('registrationIdMap') || {}), ...(storage.get('registrationIdMap') || {}),
[pni]: registrationId, [pni]: registrationId,
}), }),
async () => { (async () => {
const newId = signedPreKey.id() + 1; const newId = signedPreKey.id() + 1;
log.warn(`${logId}: Updating next signed pre key id to ${newId}`); log.warn(`${logId}: Updating next signed pre key id to ${newId}`);
await storage.put(SIGNED_PRE_KEY_ID_KEY[ServiceIdKind.PNI], newId); await storage.put(SIGNED_PRE_KEY_ID_KEY[ServiceIdKind.PNI], newId);
}, })(),
this.storeSignedPreKey( this.storeSignedPreKey(
pni, pni,
signedPreKey.id(), signedPreKey.id(),
@ -2662,14 +2662,14 @@ export class SignalProtocolStore extends EventEmitter {
true, true,
signedPreKey.timestamp() signedPreKey.timestamp()
), ),
async () => { (async () => {
if (!lastResortKyberPreKey) { if (!lastResortKyberPreKey) {
return; return;
} }
const newId = lastResortKyberPreKey.id() + 1; const newId = lastResortKyberPreKey.id() + 1;
log.warn(`${logId}: Updating next kyber pre key id to ${newId}`); log.warn(`${logId}: Updating next kyber pre key id to ${newId}`);
await storage.put(KYBER_KEY_ID_KEY[ServiceIdKind.PNI], newId); await storage.put(KYBER_KEY_ID_KEY[ServiceIdKind.PNI], newId);
}, })(),
lastResortKyberPreKeyBytes && lastResortKyberPreKey lastResortKyberPreKeyBytes && lastResortKyberPreKey
? this.storeKyberPreKeys(pni, [ ? this.storeKyberPreKeys(pni, [
{ {

View file

@ -214,7 +214,7 @@ describe('pnp/merge', function (this: Mocha.Suite) {
const notifications = window.locator('.SystemMessage'); const notifications = window.locator('.SystemMessage');
assert.strictEqual( assert.strictEqual(
await notifications.count(), await notifications.count(),
withPNIMessage && !pniSignatureVerified ? 1 : 0, withPNIMessage ? 1 : 0,
'notification count' 'notification count'
); );

View file

@ -377,9 +377,15 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
const messages = window.locator('.module-message__text'); const messages = window.locator('.module-message__text');
assert.strictEqual(await messages.count(), 3, 'messages'); assert.strictEqual(await messages.count(), 3, 'messages');
// No notifications // Merge notification
const notifications = window.locator('.SystemMessage'); const notifications = window.locator('.SystemMessage');
assert.strictEqual(await notifications.count(), 0, 'notifications'); assert.strictEqual(await notifications.count(), 1, 'notifications');
const first = await notifications.first();
assert.match(
await first.innerText(),
/Your message history with ACI Contact and their number .* has been merged./
);
assert.isEmpty(await phone.getOrphanedStorageKeys()); assert.isEmpty(await phone.getOrphanedStorageKeys());
} }

View file

@ -47,6 +47,7 @@ import {
isUntaggedPniString, isUntaggedPniString,
} from '../types/ServiceId'; } from '../types/ServiceId';
import { normalizeAci } from '../util/normalizeAci'; import { normalizeAci } from '../util/normalizeAci';
import { drop } from '../util/drop';
import { isMoreRecentThan, isOlderThan } from '../util/timestamp'; import { isMoreRecentThan, isOlderThan } from '../util/timestamp';
import { ourProfileKeyService } from '../services/ourProfileKey'; import { ourProfileKeyService } from '../services/ourProfileKey';
import { strictAssert } from '../util/assert'; import { strictAssert } from '../util/assert';
@ -558,7 +559,10 @@ export default class AccountManager extends EventTarget {
return toUpload; return toUpload;
} }
async maybeUpdateKeys(serviceIdKind: ServiceIdKind): Promise<void> { async maybeUpdateKeys(
serviceIdKind: ServiceIdKind,
forceUpdate = false
): Promise<void> {
const logId = `maybeUpdateKeys(${serviceIdKind})`; const logId = `maybeUpdateKeys(${serviceIdKind})`;
await this.queueTask(async () => { await this.queueTask(async () => {
const { storage } = window.textsecure; const { storage } = window.textsecure;
@ -583,7 +587,7 @@ export default class AccountManager extends EventTarget {
await this.server.getMyKeyCounts(serviceIdKind); await this.server.getMyKeyCounts(serviceIdKind);
let preKeys: Array<UploadPreKeyType> | undefined; let preKeys: Array<UploadPreKeyType> | undefined;
if (preKeyCount < PRE_KEY_MINIMUM) { if (preKeyCount < PRE_KEY_MINIMUM || forceUpdate) {
log.info( log.info(
`${logId}: Server prekey count is ${preKeyCount}, generating a new set` `${logId}: Server prekey count is ${preKeyCount}, generating a new set`
); );
@ -591,7 +595,7 @@ export default class AccountManager extends EventTarget {
} }
let pqPreKeys: Array<UploadKyberPreKeyType> | undefined; let pqPreKeys: Array<UploadKyberPreKeyType> | undefined;
if (kyberPreKeyCount < PRE_KEY_MINIMUM) { if (kyberPreKeyCount < PRE_KEY_MINIMUM || forceUpdate) {
log.info( log.info(
`${logId}: Server kyber prekey count is ${kyberPreKeyCount}, generating a new set` `${logId}: Server kyber prekey count is ${kyberPreKeyCount}, generating a new set`
); );
@ -599,9 +603,13 @@ export default class AccountManager extends EventTarget {
} }
const pqLastResortPreKey = await this.maybeUpdateLastResortKyberKey( const pqLastResortPreKey = await this.maybeUpdateLastResortKyberKey(
serviceIdKind serviceIdKind,
forceUpdate
);
const signedPreKey = await this.maybeUpdateSignedPreKey(
serviceIdKind,
forceUpdate
); );
const signedPreKey = await this.maybeUpdateSignedPreKey(serviceIdKind);
if ( if (
!preKeys?.length && !preKeys?.length &&
@ -615,7 +623,7 @@ export default class AccountManager extends EventTarget {
const keySummary: Array<string> = []; const keySummary: Array<string> = [];
if (preKeys?.length) { if (preKeys?.length) {
keySummary.push(`${!preKeys?.length || 0} prekeys`); keySummary.push(`${preKeys.length} prekeys`);
} }
if (signedPreKey) { if (signedPreKey) {
keySummary.push('a signed prekey'); keySummary.push('a signed prekey');
@ -624,7 +632,7 @@ export default class AccountManager extends EventTarget {
keySummary.push('a last-resort kyber prekey'); keySummary.push('a last-resort kyber prekey');
} }
if (pqPreKeys?.length) { if (pqPreKeys?.length) {
keySummary.push(`${!pqPreKeys?.length || 0} kyber prekeys`); keySummary.push(`${pqPreKeys.length} kyber prekeys`);
} }
log.info(`${logId}: Uploading with ${keySummary.join(', ')}`); log.info(`${logId}: Uploading with ${keySummary.join(', ')}`);
@ -699,7 +707,8 @@ export default class AccountManager extends EventTarget {
} }
private async maybeUpdateSignedPreKey( private async maybeUpdateSignedPreKey(
serviceIdKind: ServiceIdKind serviceIdKind: ServiceIdKind,
forceUpdate = false
): Promise<UploadSignedPreKeyType | undefined> { ): Promise<UploadSignedPreKeyType | undefined> {
const ourServiceId = const ourServiceId =
window.textsecure.storage.user.getCheckedServiceId(serviceIdKind); window.textsecure.storage.user.getCheckedServiceId(serviceIdKind);
@ -713,7 +722,10 @@ export default class AccountManager extends EventTarget {
const mostRecent = confirmedKeys[0]; const mostRecent = confirmedKeys[0];
const lastUpdate = mostRecent?.created_at; const lastUpdate = mostRecent?.created_at;
if (isMoreRecentThan(lastUpdate || 0, SIGNED_PRE_KEY_ROTATION_AGE)) { if (
!forceUpdate &&
isMoreRecentThan(lastUpdate || 0, SIGNED_PRE_KEY_ROTATION_AGE)
) {
log.warn( log.warn(
`${logId}: ${confirmedKeys.length} confirmed keys, ` + `${logId}: ${confirmedKeys.length} confirmed keys, ` +
`most recent was created ${lastUpdate}. No need to update.` `most recent was created ${lastUpdate}. No need to update.`
@ -765,7 +777,8 @@ export default class AccountManager extends EventTarget {
} }
private async maybeUpdateLastResortKyberKey( private async maybeUpdateLastResortKyberKey(
serviceIdKind: ServiceIdKind serviceIdKind: ServiceIdKind,
forceUpdate = false
): Promise<UploadSignedPreKeyType | undefined> { ): Promise<UploadSignedPreKeyType | undefined> {
const ourServiceId = const ourServiceId =
window.textsecure.storage.user.getCheckedServiceId(serviceIdKind); window.textsecure.storage.user.getCheckedServiceId(serviceIdKind);
@ -779,7 +792,10 @@ export default class AccountManager extends EventTarget {
const mostRecent = confirmedKeys[0]; const mostRecent = confirmedKeys[0];
const lastUpdate = mostRecent?.createdAt; const lastUpdate = mostRecent?.createdAt;
if (isMoreRecentThan(lastUpdate || 0, LAST_RESORT_KEY_ROTATION_AGE)) { if (
!forceUpdate &&
isMoreRecentThan(lastUpdate || 0, LAST_RESORT_KEY_ROTATION_AGE)
) {
log.warn( log.warn(
`${logId}: ${confirmedKeys.length} confirmed keys, ` + `${logId}: ${confirmedKeys.length} confirmed keys, ` +
`most recent was created ${lastUpdate}. No need to update.` `most recent was created ${lastUpdate}. No need to update.`
@ -1389,17 +1405,20 @@ export default class AccountManager extends EventTarget {
await storage.protocol.updateOurPniKeyMaterial(pni, keyMaterial); await storage.protocol.updateOurPniKeyMaterial(pni, keyMaterial);
// Intentionally not awaiting since this is processed on encrypted queue // Intentionally not awaiting since this is processed on encrypted queue
// of MessageReceiver. // of MessageReceiver. Note that `maybeUpdateKeys` runs on the queue so
void this.queueTask(async () => { // we don't have to wrap it with `queueTask`.
try { drop(
await this.maybeUpdateKeys(ServiceIdKind.PNI); (async () => {
} catch (error) { try {
log.error( await this.maybeUpdateKeys(ServiceIdKind.PNI, true);
`${logId}: Failed to upload PNI prekeys. Moving on`, } catch (error) {
Errors.toLogFormat(error) log.error(
); `${logId}: Failed to upload PNI prekeys. Moving on`,
} Errors.toLogFormat(error)
}); );
}
})()
);
// PNI has changed and credentials are no longer valid // PNI has changed and credentials are no longer valid
await storage.put('groupCredentials', []); await storage.put('groupCredentials', []);

View file

@ -3973,10 +3973,10 @@
type-fest "^3.5.0" type-fest "^3.5.0"
uuid "^8.3.0" uuid "^8.3.0"
"@signalapp/mock-server@5.0.0": "@signalapp/mock-server@5.0.1":
version "5.0.0" version "5.0.1"
resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-5.0.0.tgz#2534925b0e248b6211cda420641508d92c5a97cb" resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-5.0.1.tgz#648eb44b91f1ecbb0b3dc1791101fb5d67ac0e73"
integrity sha512-HlEgiBKmPp1Rl5ltfg/FmIfH2DQeCzb7lML8SscRvZOQSE83+MRf7JZtwIlDQxg3YmhQo0RmReQNv8yz5KtbVg== integrity sha512-KuaHznpxubFib9LJiIP6lT8Oynuni4yzrItetxVwN9YPMTlFa0TYvaf+InODnH7tnM1bY1NCku9SYy+0H2ejMQ==
dependencies: dependencies:
"@signalapp/libsignal-client" "^0.39.2" "@signalapp/libsignal-client" "^0.39.2"
debug "^4.3.2" debug "^4.3.2"