Add preliminary message backup harness

This commit is contained in:
Fedor Indutny 2024-03-15 07:20:33 -07:00 committed by GitHub
parent 231bf91a22
commit d85a1d5074
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 2997 additions and 121 deletions

View file

@ -26,6 +26,7 @@ import createTaskWithTimeout from './TaskWithTimeout';
import * as Bytes from '../Bytes';
import * as Errors from '../types/errors';
import { senderCertificateService } from '../services/senderCertificate';
import { backupsService } from '../services/backups';
import {
deriveAccessKey,
generateRegistrationId,
@ -123,6 +124,7 @@ type CreateAccountSharedOptionsType = Readonly<{
pniKeyPair: KeyPairType;
profileKey: Uint8Array;
masterKey: Uint8Array | undefined;
backupFile?: Uint8Array;
}>;
type CreatePrimaryDeviceOptionsType = Readonly<{
@ -213,6 +215,11 @@ function signedPreKeyToUploadSignedPreKey({
};
}
export type ConfirmNumberResultType = Readonly<{
deviceName: string;
backupFile: Uint8Array | undefined;
}>;
export default class AccountManager extends EventTarget {
pending: Promise<void>;
@ -339,7 +346,7 @@ export default class AccountManager extends EventTarget {
async registerSecondDevice(
setProvisioningUrl: (url: string) => void,
confirmNumber: (number?: string) => Promise<string>
confirmNumber: (number?: string) => Promise<ConfirmNumberResultType>
): Promise<void> {
const provisioningCipher = new ProvisioningCipher();
const pubKey = await provisioningCipher.getPublicKey();
@ -407,7 +414,9 @@ export default class AccountManager extends EventTarget {
const provisionMessage = await provisioningCipher.decrypt(envelope);
await this.queueTask(async () => {
const deviceName = await confirmNumber(provisionMessage.number);
const { deviceName, backupFile } = await confirmNumber(
provisionMessage.number
);
if (typeof deviceName !== 'string' || deviceName.length === 0) {
throw new Error(
'AccountManager.registerSecondDevice: Invalid device name'
@ -443,6 +452,7 @@ export default class AccountManager extends EventTarget {
pniKeyPair: provisionMessage.pniKeyPair,
profileKey: provisionMessage.profileKey,
deviceName,
backupFile,
userAgent: provisionMessage.userAgent,
ourAci,
ourPni,
@ -1018,6 +1028,7 @@ export default class AccountManager extends EventTarget {
masterKey,
readReceipts,
userAgent,
backupFile,
} = options;
const { storage } = window.textsecure;
@ -1049,7 +1060,7 @@ export default class AccountManager extends EventTarget {
const numberChanged =
!previousACI && previousNumber && previousNumber !== number;
if (uuidChanged || numberChanged) {
if (uuidChanged || numberChanged || backupFile !== undefined) {
if (uuidChanged) {
log.warn(
'createAccount: New uuid is different from old uuid; deleting all previous data'
@ -1060,6 +1071,11 @@ export default class AccountManager extends EventTarget {
'createAccount: New number is different from old number; deleting all previous data'
);
}
if (backupFile !== undefined) {
log.warn(
'createAccount: Restoring from backup; deleting all previous data'
);
}
try {
await storage.protocol.removeAllData();
@ -1200,17 +1216,13 @@ export default class AccountManager extends EventTarget {
// This needs to be done very early, because it changes how things are saved in the
// database. Your identity, for example, in the saveIdentityWithAttributes call
// below.
const { conversation } = window.ConversationController.maybeMergeContacts({
window.ConversationController.maybeMergeContacts({
aci: ourAci,
pni: ourPni,
e164: number,
reason: 'createAccount',
});
if (!conversation) {
throw new Error('registrationDone: no conversation!');
}
const identityAttrs = {
firstUse: true,
timestamp: Date.now(),
@ -1317,6 +1329,10 @@ export default class AccountManager extends EventTarget {
uploadKeys(ServiceIdKind.ACI),
uploadKeys(ServiceIdKind.PNI),
]);
if (backupFile !== undefined) {
await backupsService.importBackup(backupFile);
}
}
// Exposed only for testing