Notifications for a few merge-related scenarios

This commit is contained in:
Scott Nonnenberg 2022-12-05 14:46:54 -08:00 committed by GitHub
parent 78ce34b9d3
commit a49a6f2057
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 2764 additions and 553 deletions

View file

@ -1523,7 +1523,11 @@ export class SignalProtocolStore extends EventEmitter {
switch (direction) {
case Direction.Sending:
return this.isTrustedForSending(publicKey, identityRecord);
return this.isTrustedForSending(
encodedAddress.uuid,
publicKey,
identityRecord
);
case Direction.Receiving:
return true;
default:
@ -1533,11 +1537,31 @@ export class SignalProtocolStore extends EventEmitter {
// https://github.com/signalapp/Signal-Android/blob/fc3db538bcaa38dc149712a483d3032c9c1f3998/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalBaseIdentityKeyStore.java#L233
isTrustedForSending(
uuid: UUID,
publicKey: Uint8Array,
identityRecord?: IdentityKeyType
): boolean {
if (!identityRecord) {
log.info('isTrustedForSending: No previous record, returning true...');
// To track key changes across session switches, we save an old identity key on the
// conversation.
const conversation = window.ConversationController.get(uuid.toString());
const previousIdentityKeyBase64 = conversation?.get(
'previousIdentityKey'
);
if (conversation && previousIdentityKeyBase64) {
const previousIdentityKey = Bytes.fromBase64(previousIdentityKeyBase64);
if (!constantTimeEqual(previousIdentityKey, publicKey)) {
log.info(
'isTrustedForSending: previousIdentityKey does not match, returning false'
);
return false;
}
}
log.info(
'isTrustedForSending: No previous record or previousIdentityKey, returning true'
);
return true;
}
@ -1552,7 +1576,7 @@ export class SignalProtocolStore extends EventEmitter {
return false;
}
if (identityRecord.verified === VerifiedStatus.UNVERIFIED) {
log.error('isTrustedIdentity: Needs unverified approval!');
log.error('isTrustedForSending: Needs unverified approval!');
return false;
}
if (this.isNonBlockingApprovalRequired(identityRecord)) {
@ -1648,6 +1672,8 @@ export class SignalProtocolStore extends EventEmitter {
nonblockingApproval,
});
this.checkPreviousKey(encodedAddress.uuid, publicKey, 'saveIdentity');
return false;
}
@ -1690,7 +1716,7 @@ export class SignalProtocolStore extends EventEmitter {
// See `addKeyChange` in `ts/models/conversations.ts` for sender key info
// update caused by this.
try {
this.emit('keychange', encodedAddress.uuid);
this.emit('keychange', encodedAddress.uuid, 'saveIdentity - change');
} catch (error) {
log.error(
'saveIdentity: error triggering keychange:',
@ -1822,6 +1848,37 @@ export class SignalProtocolStore extends EventEmitter {
return VerifiedStatus.DEFAULT;
}
// To track key changes across session switches, we save an old identity key on the
// conversation. Whenever we get a new identity key for that contact, we need to
// check it against that saved key - no need to pop a key change warning if it is
// the same!
checkPreviousKey(uuid: UUID, publicKey: Uint8Array, context: string): void {
const conversation = window.ConversationController.get(uuid.toString());
const previousIdentityKeyBase64 = conversation?.get('previousIdentityKey');
if (conversation && previousIdentityKeyBase64) {
const previousIdentityKey = Bytes.fromBase64(previousIdentityKeyBase64);
try {
if (!constantTimeEqual(previousIdentityKey, publicKey)) {
this.emit(
'keychange',
uuid,
`${context} - previousIdentityKey check`
);
}
// We only want to clear previousIdentityKey on a match, or on successfully emit.
conversation.set({ previousIdentityKey: undefined });
window.Signal.Data.updateConversation(conversation.attributes);
} catch (error) {
log.error(
'saveIdentity: error triggering keychange:',
error && error.stack ? error.stack : error
);
}
}
}
// See https://github.com/signalapp/Signal-Android/blob/fc3db538bcaa38dc149712a483d3032c9c1f3998/app/src/main/java/org/thoughtcrime/securesms/database/IdentityDatabase.java#L184
async updateIdentityAfterSync(
uuid: UUID,
@ -1851,10 +1908,11 @@ export class SignalProtocolStore extends EventEmitter {
nonblockingApproval: true,
});
}
if (hadEntry && !keyMatches) {
if (!hadEntry) {
this.checkPreviousKey(uuid, publicKey, 'updateIdentityAfterSync');
} else if (hadEntry && !keyMatches) {
try {
this.emit('keychange', uuid);
this.emit('keychange', uuid, 'updateIdentityAfterSync - change');
} catch (error) {
log.error(
'updateIdentityAfterSync: error triggering keychange:',
@ -1903,7 +1961,10 @@ export class SignalProtocolStore extends EventEmitter {
return false;
}
async removeIdentityKey(uuid: UUID): Promise<void> {
async removeIdentityKey(
uuid: UUID,
options?: { disableSessionDeletion: boolean }
): Promise<void> {
if (!this.identityKeys) {
throw new Error('removeIdentityKey: this.identityKeys not yet cached!');
}
@ -1911,7 +1972,9 @@ export class SignalProtocolStore extends EventEmitter {
const id = uuid.toString();
this.identityKeys.delete(id);
await window.Signal.Data.removeIdentityKeyById(id);
await this.removeAllSessions(id);
if (!options?.disableSessionDeletion) {
await this.removeAllSessions(id);
}
}
// Not yet processed messages - for resiliency
@ -2197,7 +2260,7 @@ export class SignalProtocolStore extends EventEmitter {
public override on(
name: 'keychange',
handler: (theirUuid: UUID) => unknown
handler: (theirUuid: UUID, reason: string) => unknown
): this;
public override on(name: 'removeAllData', handler: () => unknown): this;
@ -2212,7 +2275,11 @@ export class SignalProtocolStore extends EventEmitter {
public override emit(name: 'removePreKey', ourUuid: UUID): boolean;
public override emit(name: 'keychange', theirUuid: UUID): boolean;
public override emit(
name: 'keychange',
theirUuid: UUID,
reason: string
): boolean;
public override emit(name: 'removeAllData'): boolean;