Page MenuHomePhabricator

D14047.id46081.diff
No OneTemporary

D14047.id46081.diff

diff --git a/native/backup/restore-siwe-backup.react.js b/native/backup/restore-siwe-backup.react.js
--- a/native/backup/restore-siwe-backup.react.js
+++ b/native/backup/restore-siwe-backup.react.js
@@ -4,11 +4,10 @@
import { Alert } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
-import { userKeysResponseValidator } from 'lib/types/backup-types.js';
import { type SIWEResult } from 'lib/types/siwe-types.js';
import { getMessageForException } from 'lib/utils/errors.js';
-import { assertWithValidator } from 'lib/utils/validation-utils.js';
+import { useClientBackup } from './use-client-backup.js';
import { SignSIWEBackupMessageForRestore } from '../account/registration/siwe-backup-message-creation.react.js';
import { commCoreModule } from '../native-modules.js';
import { type RootNavigationProp } from '../navigation/root-navigator.react.js';
@@ -43,25 +42,23 @@
},
} = route;
+ const { getBackupUserKeys } = useClientBackup();
+
const onSuccessfulWalletSignature = React.useCallback(
(result: SIWEResult) => {
void (async () => {
const { signature } = result;
let message = 'success';
try {
- const userKeysResponse = commCoreModule.getBackupUserKeys(
+ const { backupDataKey, backupLogDataKey } = await getBackupUserKeys(
userIdentifier,
signature,
backupID,
);
- const userKeys = assertWithValidator(
- userKeysResponse,
- userKeysResponseValidator,
- );
await commCoreModule.restoreBackupData(
backupID,
- userKeys.backupDataKey,
- userKeys.backupLogDataKey,
+ backupDataKey,
+ backupLogDataKey,
persistConfig.version.toString(),
);
} catch (e) {
@@ -74,7 +71,7 @@
goBack();
})();
},
- [backupID, goBack, userIdentifier],
+ [backupID, getBackupUserKeys, goBack, userIdentifier],
);
return (
diff --git a/native/backup/use-client-backup.js b/native/backup/use-client-backup.js
--- a/native/backup/use-client-backup.js
+++ b/native/backup/use-client-backup.js
@@ -6,6 +6,8 @@
import {
latestBackupInfoResponseValidator,
type LatestBackupInfo,
+ type UserKeys,
+ userKeysResponseValidator,
} from 'lib/types/backup-types.js';
import { assertWithValidator } from 'lib/utils/validation-utils.js';
@@ -20,8 +22,29 @@
+latestBackupInfo: LatestBackupInfo,
+userIdentifier: string,
}>,
+ +getBackupUserKeys: (
+ userIdentifier: string,
+ backupSecret: string,
+ backupID: string,
+ ) => Promise<UserKeys>,
};
+async function getBackupUserKeys(
+ userIdentifier: string,
+ backupSecret: string,
+ backupID: string,
+): Promise<UserKeys> {
+ const userKeysResponse = await commCoreModule.getBackupUserKeys(
+ userIdentifier,
+ backupSecret,
+ backupID,
+ );
+ return assertWithValidator(
+ JSON.parse(userKeysResponse),
+ userKeysResponseValidator,
+ );
+}
+
function useClientBackup(): ClientBackup {
const currentUserID = useSelector(
state => state.currentUserInfo && state.currentUserInfo.id,
@@ -69,8 +92,9 @@
createFullBackup,
createUserKeysBackup,
retrieveLatestBackupInfo,
+ getBackupUserKeys,
}),
- [retrieveLatestBackupInfo, createFullBackup, createUserKeysBackup],
+ [createFullBackup, createUserKeysBackup, retrieveLatestBackupInfo],
);
}
diff --git a/native/profile/backup-menu.react.js b/native/profile/backup-menu.react.js
--- a/native/profile/backup-menu.react.js
+++ b/native/profile/backup-menu.react.js
@@ -1,15 +1,17 @@
// @flow
import { useNavigation } from '@react-navigation/native';
+import invariant from 'invariant';
import * as React from 'react';
import { Switch, Text, View } from 'react-native';
import { ScrollView } from 'react-native-gesture-handler';
import { accountHasPassword } from 'lib/shared/account-utils.js';
-import { userKeysResponseValidator } from 'lib/types/backup-types.js';
+import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
+import { getConfig } from 'lib/utils/config.js';
+import { rawDeviceListFromSignedList } from 'lib/utils/device-list-utils.js';
import { getMessageForException } from 'lib/utils/errors.js';
import { useDispatch } from 'lib/utils/redux-utils.js';
-import { assertWithValidator } from 'lib/utils/validation-utils.js';
import type { ProfileNavigationProp } from './profile.react.js';
import { useClientBackup } from '../backup/use-client-backup.js';
@@ -41,8 +43,16 @@
state => state.localSettings.isBackupEnabled,
);
- const { createFullBackup, retrieveLatestBackupInfo, createUserKeysBackup } =
- useClientBackup();
+ const identityContext = React.useContext(IdentityClientContext);
+ invariant(identityContext, 'Identity context should be set');
+ const { identityClient, getAuthMetadata } = identityContext;
+
+ const {
+ createFullBackup,
+ retrieveLatestBackupInfo,
+ createUserKeysBackup,
+ getBackupUserKeys,
+ } = useClientBackup();
const uploadBackup = React.useCallback(async () => {
let message;
@@ -73,19 +83,15 @@
try {
const [{ latestBackupInfo, userIdentifier }, backupSecret] =
await Promise.all([retrieveLatestBackupInfo(), getBackupSecret()]);
- const userKeysResponse = await commCoreModule.getBackupUserKeys(
+ const { backupDataKey, backupLogDataKey } = await getBackupUserKeys(
userIdentifier,
backupSecret,
latestBackupInfo.backupID,
);
- const userKeys = assertWithValidator(
- JSON.parse(userKeysResponse),
- userKeysResponseValidator,
- );
await commCoreModule.restoreBackupData(
latestBackupInfo.backupID,
- userKeys.backupDataKey,
- userKeys.backupLogDataKey,
+ backupDataKey,
+ backupLogDataKey,
persistConfig.version.toString(),
);
console.info('Backup restored.');
@@ -94,7 +100,7 @@
console.error(message);
}
Alert.alert('Restore protocol result', message);
- }, [getBackupSecret, retrieveLatestBackupInfo]);
+ }, [getBackupSecret, getBackupUserKeys, retrieveLatestBackupInfo]);
const testLatestBackupInfo = React.useCallback(async () => {
let message;
@@ -115,6 +121,102 @@
Alert.alert('Latest backup info result', message);
}, [currentUserInfo?.id, retrieveLatestBackupInfo]);
+ const testSigning = React.useCallback(async () => {
+ // This test only works in the following case:
+ // 1. Logged in on Primary Device using v1
+ // 2. Creating User Keys Backup on Primary
+ // 3. Log Out on Primary Device using v1
+ // 4. Log In on any native device using v1
+ // 5. Perform this test
+ let message;
+ try {
+ const {
+ latestBackupInfo: { userID, backupID },
+ userIdentifier,
+ } = await retrieveLatestBackupInfo();
+
+ if (currentUserInfo?.id !== userID) {
+ throw new Error('Backup returned different userID');
+ }
+
+ // We fetch Device List history to get previous primary `deviceID`
+ const deviceLists =
+ await identityClient.getDeviceListHistoryForUser(userID);
+ if (deviceLists.length < 3) {
+ throw new Error(
+ 'Previous Primary Device issue: device list history too short',
+ );
+ }
+
+ // According to steps listed above, device list history looks like this:
+ // 1. [...], [lastPrimaryDeviceID]
+ // 2. [...], [lastPrimaryDeviceID]
+ // 3. [...], [lastPrimaryDeviceID], []
+ // 4. [...], [lastPrimaryDeviceID], [], [currentPrimaryDeviceID]
+ // 5. [...], [lastPrimaryDeviceID], [], [currentPrimaryDeviceID]
+ // In order to get lastPrimaryDeviceID, we need to get the last
+ // but two item
+ const lastDeviceListWithPrimary = deviceLists[deviceLists.length - 3];
+ const lastRawDeviceListWithPrimary = rawDeviceListFromSignedList(
+ lastDeviceListWithPrimary,
+ );
+ const lastPrimaryDeviceID = lastRawDeviceListWithPrimary.devices[0];
+ if (!lastPrimaryDeviceID) {
+ throw new Error('Previous Primary Device issue: empty device list');
+ }
+
+ const { deviceID } = await getAuthMetadata();
+ if (deviceID === lastPrimaryDeviceID) {
+ throw new Error('Previous Primary Device issue: the same deviceIDs');
+ }
+
+ const backupSecret = await getBackupSecret();
+ const { pickledAccount, pickleKey } = await getBackupUserKeys(
+ userIdentifier,
+ backupSecret,
+ backupID,
+ );
+
+ const emptyDeviceListMessage = '[]';
+
+ // Sign using Olm Account from backup
+ const signature = await commCoreModule.signMessageUsingAccount(
+ emptyDeviceListMessage,
+ pickledAccount,
+ pickleKey,
+ );
+
+ // Verify using previous primary `deviceID`
+ const { olmAPI } = getConfig();
+ const verificationResult = await olmAPI.verifyMessage(
+ emptyDeviceListMessage,
+ signature,
+ lastPrimaryDeviceID,
+ );
+
+ message =
+ `Backup ID: ${backupID},\n` +
+ `userID: ${userID},\n` +
+ `deviceID: ${deviceID ?? ''},\n` +
+ `lastPrimaryDeviceID: ${lastPrimaryDeviceID},\n` +
+ `signature: ${signature},\n` +
+ `verificationResult: ${verificationResult.toString()}\n`;
+ } catch (e) {
+ message = `Latest backup info error: ${String(
+ getMessageForException(e),
+ )}`;
+ console.error(message);
+ }
+ Alert.alert('Signing with previous primary Olm Account result', message);
+ }, [
+ currentUserInfo?.id,
+ getAuthMetadata,
+ getBackupSecret,
+ getBackupUserKeys,
+ identityClient,
+ retrieveLatestBackupInfo,
+ ]);
+
const testRestoreForSIWEUser = React.useCallback(async () => {
let message = 'success';
try {
@@ -222,6 +324,19 @@
</Text>
</Button>
</View>
+ <View style={styles.section}>
+ <Button
+ onPress={testSigning}
+ style={styles.row}
+ iosFormat="highlight"
+ iosHighlightUnderlayColor={colors.panelIosHighlightUnderlay}
+ iosActiveOpacity={0.85}
+ >
+ <Text style={styles.submenuText}>
+ Test signing with previous primary Olm Account
+ </Text>
+ </Button>
+ </View>
</ScrollView>
);
}

File Metadata

Mime Type
text/plain
Expires
Mon, Dec 2, 1:21 PM (7 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2607688
Default Alt Text
D14047.id46081.diff (10 KB)

Event Timeline