favalib 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/Command/BaseCommand.d.mts +2 -2
- package/build/Command/commandConstructors.d.mts +2 -0
- package/build/Command/commandConstructors.mjs +2 -0
- package/build/Command/commands/AddSyncDeviceCommand.d.mts +3 -4
- package/build/Command/commands/AddSyncDeviceCommand.mjs +2 -2
- package/build/Command/commands/ChangeSyncDeviceMetaCommand.d.mts +32 -0
- package/build/Command/commands/ChangeSyncDeviceMetaCommand.mjs +37 -0
- package/build/TwoFaLib.d.mts +3 -7
- package/build/TwoFaLib.mjs +8 -11
- package/build/interfaces/CommandTypes.d.mts +5 -1
- package/build/interfaces/SyncTypes.d.mts +7 -5
- package/build/main.d.mts +2 -2
- package/build/subclasses/CommandManager.mjs +9 -2
- package/build/subclasses/PersistentStorageManager.mjs +2 -1
- package/build/subclasses/SyncManager.d.mts +21 -8
- package/build/subclasses/SyncManager.mjs +104 -50
- package/build/subclasses/VaultDataManager.d.mts +3 -2
- package/build/subclasses/VaultDataManager.mjs +10 -3
- package/package.json +1 -1
|
@@ -30,9 +30,9 @@ declare abstract class BaseCommand<T extends CommandData = CommandData> {
|
|
|
30
30
|
/**
|
|
31
31
|
* Creates an undo command that, when executed, reverses the effects of this command.
|
|
32
32
|
* @param VaultDataManager - The TwoFaLibMediator instance to use for creating the undo command.
|
|
33
|
-
* @returns A BaseCommand instance that undoes this command.
|
|
33
|
+
* @returns A BaseCommand instance that undoes this command or false if this command has no undo.
|
|
34
34
|
*/
|
|
35
|
-
abstract createUndoCommand(TwoFaLibMediator: TwoFaLibMediator): BaseCommand;
|
|
35
|
+
abstract createUndoCommand(TwoFaLibMediator: TwoFaLibMediator): BaseCommand | false;
|
|
36
36
|
/**
|
|
37
37
|
* Creates a new instance of the command with the provided data.
|
|
38
38
|
* @param data - The data to use for creating the new command instance.
|
|
@@ -2,10 +2,12 @@ import AddEntryCommand from './commands/AddEntryCommand.mjs';
|
|
|
2
2
|
import AddSyncDeviceCommand from './commands/AddSyncDeviceCommand.mjs';
|
|
3
3
|
import DeleteEntryCommand from './commands/DeleteEntryCommand.mjs';
|
|
4
4
|
import UpdateEntryCommand from './commands/UpdateEntryCommand.mjs';
|
|
5
|
+
import ChangeSyncDeviceMetaCommand from './commands/ChangeSyncDeviceMetaCommand.mjs';
|
|
5
6
|
declare const commandConstructors: {
|
|
6
7
|
AddEntry: typeof AddEntryCommand;
|
|
7
8
|
DeleteEntry: typeof DeleteEntryCommand;
|
|
8
9
|
UpdateEntry: typeof UpdateEntryCommand;
|
|
9
10
|
AddSyncDevice: typeof AddSyncDeviceCommand;
|
|
11
|
+
ChangeSyncDeviceMeta: typeof ChangeSyncDeviceMetaCommand;
|
|
10
12
|
};
|
|
11
13
|
export default commandConstructors;
|
|
@@ -2,10 +2,12 @@ import AddEntryCommand from './commands/AddEntryCommand.mjs';
|
|
|
2
2
|
import AddSyncDeviceCommand from './commands/AddSyncDeviceCommand.mjs';
|
|
3
3
|
import DeleteEntryCommand from './commands/DeleteEntryCommand.mjs';
|
|
4
4
|
import UpdateEntryCommand from './commands/UpdateEntryCommand.mjs';
|
|
5
|
+
import ChangeSyncDeviceMetaCommand from './commands/ChangeSyncDeviceMetaCommand.mjs';
|
|
5
6
|
const commandConstructors = {
|
|
6
7
|
AddEntry: AddEntryCommand,
|
|
7
8
|
DeleteEntry: DeleteEntryCommand,
|
|
8
9
|
UpdateEntry: UpdateEntryCommand,
|
|
9
10
|
AddSyncDevice: AddSyncDeviceCommand,
|
|
11
|
+
ChangeSyncDeviceMeta: ChangeSyncDeviceMetaCommand,
|
|
10
12
|
};
|
|
11
13
|
export default commandConstructors;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type TwoFaLibMediator from '../../TwoFaLibMediator.mjs';
|
|
2
2
|
import Command from '../BaseCommand.mjs';
|
|
3
|
-
import type { DeviceId
|
|
3
|
+
import type { DeviceId } from '../../interfaces/SyncTypes.mjs';
|
|
4
4
|
import type { PublicKey } from '../../interfaces/CryptoLib.mjs';
|
|
5
5
|
export interface AddSyncDeviceData {
|
|
6
6
|
deviceId: DeviceId;
|
|
7
|
-
deviceType: DeviceType;
|
|
8
7
|
publicKey: PublicKey;
|
|
9
8
|
}
|
|
10
9
|
/**
|
|
@@ -12,13 +11,13 @@ export interface AddSyncDeviceData {
|
|
|
12
11
|
*/
|
|
13
12
|
declare class AddSyncDeviceCommand extends Command<AddSyncDeviceData> {
|
|
14
13
|
/**
|
|
15
|
-
* Creates a new
|
|
14
|
+
* Creates a new AddSyncDeviceCommand instance.
|
|
16
15
|
* @inheritdoc
|
|
17
16
|
* @param data - The data of the entry to be added.
|
|
18
17
|
*/
|
|
19
18
|
constructor(data: AddSyncDeviceData, id?: string, timestamp?: number, version?: string, fromRemote?: boolean);
|
|
20
19
|
/**
|
|
21
|
-
* Executes the command to add
|
|
20
|
+
* Executes the command to add a sync device
|
|
22
21
|
* @inheritdoc
|
|
23
22
|
* @throws {InvalidCommandError} If the command data is invalid.
|
|
24
23
|
*/
|
|
@@ -5,7 +5,7 @@ import Command from '../BaseCommand.mjs';
|
|
|
5
5
|
*/
|
|
6
6
|
class AddSyncDeviceCommand extends Command {
|
|
7
7
|
/**
|
|
8
|
-
* Creates a new
|
|
8
|
+
* Creates a new AddSyncDeviceCommand instance.
|
|
9
9
|
* @inheritdoc
|
|
10
10
|
* @param data - The data of the entry to be added.
|
|
11
11
|
*/
|
|
@@ -13,7 +13,7 @@ class AddSyncDeviceCommand extends Command {
|
|
|
13
13
|
super('AddSyncDevice', data, id, timestamp, version, fromRemote);
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* Executes the command to add
|
|
16
|
+
* Executes the command to add a sync device
|
|
17
17
|
* @inheritdoc
|
|
18
18
|
* @throws {InvalidCommandError} If the command data is invalid.
|
|
19
19
|
*/
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type TwoFaLibMediator from '../../TwoFaLibMediator.mjs';
|
|
2
|
+
import Command from '../BaseCommand.mjs';
|
|
3
|
+
import type { DeviceFriendlyName, DeviceId, DeviceType } from '../../interfaces/SyncTypes.mjs';
|
|
4
|
+
export interface ChangeSyncDeviceMetaData {
|
|
5
|
+
deviceId: DeviceId;
|
|
6
|
+
newMeta: {
|
|
7
|
+
deviceFriendlyName: DeviceFriendlyName;
|
|
8
|
+
deviceType: DeviceType;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Represents a command that when executed changes the meta info of a sync device
|
|
13
|
+
*/
|
|
14
|
+
declare class ChangeSyncDeviceMetaCommand extends Command<ChangeSyncDeviceMetaData> {
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new ChangeSyncDeviceMetaCommand instance.
|
|
17
|
+
* @inheritdoc
|
|
18
|
+
* @param data - The id of the device to change and the new meta info
|
|
19
|
+
*/
|
|
20
|
+
constructor(data: ChangeSyncDeviceMetaData, id?: string, timestamp?: number, version?: string, fromRemote?: boolean);
|
|
21
|
+
/**
|
|
22
|
+
* Executes the command to change the sync device metadata
|
|
23
|
+
* @inheritdoc
|
|
24
|
+
* @throws {InvalidCommandError} If the referenced sync device cannot be found
|
|
25
|
+
*/
|
|
26
|
+
execute(mediator: TwoFaLibMediator): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* @inheritdoc
|
|
29
|
+
*/
|
|
30
|
+
createUndoCommand(): Command;
|
|
31
|
+
}
|
|
32
|
+
export default ChangeSyncDeviceMetaCommand;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { InvalidCommandError, TwoFALibError } from '../../TwoFALibError.mjs';
|
|
2
|
+
import Command from '../BaseCommand.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a command that when executed changes the meta info of a sync device
|
|
5
|
+
*/
|
|
6
|
+
class ChangeSyncDeviceMetaCommand extends Command {
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new ChangeSyncDeviceMetaCommand instance.
|
|
9
|
+
* @inheritdoc
|
|
10
|
+
* @param data - The id of the device to change and the new meta info
|
|
11
|
+
*/
|
|
12
|
+
constructor(data, id, timestamp, version, fromRemote = false) {
|
|
13
|
+
super('ChangeSyncDeviceMeta', data, id, timestamp, version, fromRemote);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Executes the command to change the sync device metadata
|
|
17
|
+
* @inheritdoc
|
|
18
|
+
* @throws {InvalidCommandError} If the referenced sync device cannot be found
|
|
19
|
+
*/
|
|
20
|
+
execute(mediator) {
|
|
21
|
+
const syncManager = mediator.getComponent('syncManager');
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
23
|
+
const device = syncManager['syncDevices'].find((d) => d.deviceId === this.data.deviceId);
|
|
24
|
+
if (!device) {
|
|
25
|
+
throw new InvalidCommandError('Trying to change meta of device that is not found');
|
|
26
|
+
}
|
|
27
|
+
device.meta = this.data.newMeta;
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @inheritdoc
|
|
32
|
+
*/
|
|
33
|
+
createUndoCommand() {
|
|
34
|
+
throw new TwoFALibError('Not implemented yet');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export default ChangeSyncDeviceMetaCommand;
|
package/build/TwoFaLib.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TypedEventTarget } from 'typescript-event-target';
|
|
2
2
|
import type CryptoLib from './interfaces/CryptoLib.mjs';
|
|
3
3
|
import type { EncryptedPrivateKey, EncryptedSymmetricKey, Passphrase, PrivateKey, PublicKey, Salt, SymmetricKey } from './interfaces/CryptoLib.mjs';
|
|
4
|
-
import type { DeviceId, DeviceType } from './interfaces/SyncTypes.mjs';
|
|
4
|
+
import type { DeviceFriendlyName, DeviceId, DeviceType } from './interfaces/SyncTypes.mjs';
|
|
5
5
|
import type { TwoFaLibEventMapEvents } from './interfaces/Events.mjs';
|
|
6
6
|
import type { PassphraseExtraDict } from './interfaces/PassphraseExtraDict.js';
|
|
7
7
|
import type { Vault, VaultSyncState } from './interfaces/Vault.mjs';
|
|
@@ -15,13 +15,14 @@ declare class TwoFaLib extends TypedEventTarget<TwoFaLibEventMapEvents> {
|
|
|
15
15
|
static readonly version = "0.0.1";
|
|
16
16
|
readonly deviceId: DeviceId;
|
|
17
17
|
readonly deviceType: DeviceType;
|
|
18
|
+
deviceFriendlyName: DeviceFriendlyName;
|
|
18
19
|
private mediator;
|
|
19
20
|
private readonly publicKey;
|
|
20
21
|
private readonly privateKey;
|
|
21
22
|
readonly ready: Promise<unknown>;
|
|
22
23
|
/**
|
|
23
24
|
* Constructs a new instance of TwoFaLib. If a serverUrl is provided, the library will use it for its sync operations.
|
|
24
|
-
* @param deviceType -
|
|
25
|
+
* @param deviceType - The identifier for this device type (e.g. 2fa-cli).
|
|
25
26
|
* @param cryptoLib - An instance of CryptoLib that is compatible with the environment.
|
|
26
27
|
* @param passphraseExtraDict - Additional words to be used for passphrase strength evaluation.
|
|
27
28
|
* @param privateKey - The private key used for cryptographic operations.
|
|
@@ -75,11 +76,6 @@ declare class TwoFaLib extends TypedEventTarget<TwoFaLibEventMapEvents> {
|
|
|
75
76
|
* @param force - Force setting the sync server url, even if no connection can be made
|
|
76
77
|
*/
|
|
77
78
|
setSyncServerUrl(serverUrl: string, force?: boolean): Promise<void>;
|
|
78
|
-
/**
|
|
79
|
-
* Sets the sync state, this will initiate the sync manager instance.
|
|
80
|
-
* @param syncState - The state of the sync.
|
|
81
|
-
*/
|
|
82
|
-
private setSyncState;
|
|
83
79
|
/**
|
|
84
80
|
* Dispatches a library event.
|
|
85
81
|
* @param eventType - The type of the event to dispatch, uses the TwoFaLibEvent enum.
|
package/build/TwoFaLib.mjs
CHANGED
|
@@ -17,7 +17,7 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
17
17
|
static { this.version = '0.0.1'; }
|
|
18
18
|
/**
|
|
19
19
|
* Constructs a new instance of TwoFaLib. If a serverUrl is provided, the library will use it for its sync operations.
|
|
20
|
-
* @param deviceType -
|
|
20
|
+
* @param deviceType - The identifier for this device type (e.g. 2fa-cli).
|
|
21
21
|
* @param cryptoLib - An instance of CryptoLib that is compatible with the environment.
|
|
22
22
|
* @param passphraseExtraDict - Additional words to be used for passphrase strength evaluation.
|
|
23
23
|
* @param privateKey - The private key used for cryptographic operations.
|
|
@@ -35,6 +35,7 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
35
35
|
*/
|
|
36
36
|
constructor(deviceType, cryptoLib, passphraseExtraDict, privateKey, symmetricKey, encryptedPrivateKey, encryptedSymmetricKey, salt, publicKey, deviceId, vault, syncState) {
|
|
37
37
|
super();
|
|
38
|
+
this.deviceFriendlyName = '';
|
|
38
39
|
if (!deviceType) {
|
|
39
40
|
throw new InitializationError('Device type is required');
|
|
40
41
|
}
|
|
@@ -72,7 +73,8 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
72
73
|
this.mediator.getComponent('vaultDataManager').replaceVault(vault);
|
|
73
74
|
}
|
|
74
75
|
if (syncState?.serverUrl) {
|
|
75
|
-
|
|
76
|
+
// Initiate the syncManager
|
|
77
|
+
this.mediator.registerComponent('syncManager', new SyncManager(this.mediator, this.publicKey, this.privateKey, syncState, this.deviceId));
|
|
76
78
|
}
|
|
77
79
|
else {
|
|
78
80
|
// If no syncmanager we're ready now, otherwise the syncmanager is responsible for emitting the ready event
|
|
@@ -151,7 +153,7 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
151
153
|
devices: [],
|
|
152
154
|
commandSendQueue: [],
|
|
153
155
|
};
|
|
154
|
-
const newSyncManager = new SyncManager(this.mediator, this.
|
|
156
|
+
const newSyncManager = new SyncManager(this.mediator, this.publicKey, this.privateKey, newSyncState, this.deviceId);
|
|
155
157
|
const success = await new Promise((resolve) => {
|
|
156
158
|
this.addEventListener(TwoFaLibEvent.ConnectionToSyncServerStatusChanged, (event) => {
|
|
157
159
|
if (event.detail.newStatus === ConnectionStatus.CONNECTED) {
|
|
@@ -175,18 +177,13 @@ class TwoFaLib extends TypedEventTarget {
|
|
|
175
177
|
throw new SyncError(`Failed to connect to server at ${serverUrl}, not setting`);
|
|
176
178
|
}
|
|
177
179
|
}
|
|
178
|
-
// connection succeeded (or force=true)
|
|
180
|
+
// connection succeeded (or force=true)
|
|
181
|
+
// switch to the new syncManager
|
|
179
182
|
this.mediator.unRegisterComponent('syncManager');
|
|
180
183
|
this.mediator.registerComponent('syncManager', newSyncManager);
|
|
184
|
+
// save
|
|
181
185
|
await this.forceSave();
|
|
182
186
|
}
|
|
183
|
-
/**
|
|
184
|
-
* Sets the sync state, this will initiate the sync manager instance.
|
|
185
|
-
* @param syncState - The state of the sync.
|
|
186
|
-
*/
|
|
187
|
-
setSyncState(syncState) {
|
|
188
|
-
this.mediator.registerComponent('syncManager', new SyncManager(this.mediator, this.deviceType, this.publicKey, this.privateKey, syncState, this.deviceId));
|
|
189
|
-
}
|
|
190
187
|
/**
|
|
191
188
|
* Dispatches a library event.
|
|
192
189
|
* @param eventType - The type of the event to dispatch, uses the TwoFaLibEvent enum.
|
|
@@ -2,6 +2,7 @@ import type { AddEntryData } from '../Command/commands/AddEntryCommand.mjs';
|
|
|
2
2
|
import type { DeleteEntryData } from '../Command/commands/DeleteEntryCommand.mjs';
|
|
3
3
|
import type { UpdateEntryData } from '../Command/commands/UpdateEntryCommand.mjs';
|
|
4
4
|
import type { AddSyncDeviceData } from '../Command/commands/AddSyncDeviceCommand.mjs';
|
|
5
|
+
import type { ChangeSyncDeviceMetaData } from '../Command/commands/ChangeSyncDeviceMetaCommand.mjs';
|
|
5
6
|
export type SyncCommand = ({
|
|
6
7
|
type: 'AddEntry';
|
|
7
8
|
data: AddEntryData;
|
|
@@ -14,7 +15,10 @@ export type SyncCommand = ({
|
|
|
14
15
|
} | {
|
|
15
16
|
type: 'AddSyncDevice';
|
|
16
17
|
data: AddSyncDeviceData;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'ChangeSyncDeviceMeta';
|
|
20
|
+
data: ChangeSyncDeviceMetaData;
|
|
17
21
|
}) & {
|
|
18
22
|
id: string;
|
|
19
23
|
};
|
|
20
|
-
export type CommandData = AddEntryData | DeleteEntryData | UpdateEntryData | AddSyncDeviceData;
|
|
24
|
+
export type CommandData = AddEntryData | DeleteEntryData | UpdateEntryData | AddSyncDeviceData | ChangeSyncDeviceMetaData;
|
|
@@ -3,11 +3,16 @@ import type { JPakeThreePass, Round1Result } from 'jpake-ts';
|
|
|
3
3
|
import { PublicKey, SyncKey } from './CryptoLib.mjs';
|
|
4
4
|
export type DeviceId = Tagged<string, 'DeviceId'>;
|
|
5
5
|
export type DeviceType = Tagged<string, 'DeviceType'>;
|
|
6
|
+
export type DeviceFriendlyName = Tagged<string, 'DeviceFriendlyName'>;
|
|
6
7
|
export interface SyncDevice {
|
|
7
8
|
deviceId: DeviceId;
|
|
8
|
-
deviceType: DeviceType;
|
|
9
9
|
publicKey: PublicKey;
|
|
10
|
+
meta?: {
|
|
11
|
+
deviceType: DeviceType;
|
|
12
|
+
deviceFriendlyName: DeviceFriendlyName;
|
|
13
|
+
};
|
|
10
14
|
}
|
|
15
|
+
export type PublicSyncDevice = Omit<SyncDevice, 'publicKey'>;
|
|
11
16
|
export interface BaseAddDeviceFlow {
|
|
12
17
|
jpak: JPakeThreePass;
|
|
13
18
|
addDevicePassword: Uint8Array;
|
|
@@ -22,14 +27,12 @@ export interface AddDeviceFlowInitiator_Initiated extends BaseAddDeviceFlow {
|
|
|
22
27
|
export interface AddDeviceFlowInitiator_SyncKeyCreated extends Omit<AddDeviceFlowInitiator_Initiated, 'state' | 'resolveContinuePromise'> {
|
|
23
28
|
state: 'initiator:syncKeyCreated';
|
|
24
29
|
responderDeviceId: DeviceId;
|
|
25
|
-
responderDeviceType: DeviceType;
|
|
26
30
|
syncKey: SyncKey;
|
|
27
31
|
}
|
|
28
32
|
export interface AddDeviceFlowResponder_Initiated extends BaseAddDeviceFlow {
|
|
29
33
|
state: 'responder:initated';
|
|
30
|
-
initiatorDeviceId: DeviceId;
|
|
31
34
|
responderDeviceId: DeviceId;
|
|
32
|
-
|
|
35
|
+
initiatorDeviceId: DeviceId;
|
|
33
36
|
}
|
|
34
37
|
export interface AddDeviceFlowResponder_SyncKeyCreated extends Omit<AddDeviceFlowResponder_Initiated, 'state'> {
|
|
35
38
|
state: 'responder:syncKeyCreated';
|
|
@@ -39,7 +42,6 @@ export type ActiveAddDeviceFlow = AddDeviceFlowInitiator_Initiated | AddDeviceFl
|
|
|
39
42
|
export interface InitiateAddDeviceFlowResult {
|
|
40
43
|
addDevicePassword: string;
|
|
41
44
|
initiatorDeviceId: DeviceId;
|
|
42
|
-
initiatorDeviceType: DeviceType;
|
|
43
45
|
timestamp: number;
|
|
44
46
|
pass1Result: Record<keyof Round1Result, string>;
|
|
45
47
|
}
|
package/build/main.d.mts
CHANGED
|
@@ -3,10 +3,10 @@ import type Entry from './interfaces/Entry.mjs';
|
|
|
3
3
|
import type { EntryId, NewEntry, EntryMeta, EntryType, TotpPayload, Token, EntryMetaWithToken } from './interfaces/Entry.mjs';
|
|
4
4
|
import type CryptoLib from './interfaces/CryptoLib.mjs';
|
|
5
5
|
import type { Encrypted, EncryptedPrivateKey, EncryptedSymmetricKey, EncryptedPublicKey, PrivateKey, SymmetricKey, PublicKey, Passphrase, Salt } from './interfaces/CryptoLib.mjs';
|
|
6
|
-
import type {
|
|
6
|
+
import type { PublicSyncDevice, DeviceId, DeviceType, DeviceFriendlyName } from './interfaces/SyncTypes.mjs';
|
|
7
7
|
import type { EncryptedVaultStateString, LockedRepresentationString } from './interfaces/Vault.mjs';
|
|
8
8
|
import { TwoFALibError, InitializationError, AuthenticationError, EntryNotFoundError, TokenGenerationError } from './TwoFALibError.mjs';
|
|
9
9
|
import { TwoFaLibEvent } from './TwoFaLibEvent.mjs';
|
|
10
10
|
import { getTwoFaLibVaultCreationUtils } from './utils/creationUtils.mjs';
|
|
11
11
|
export { TwoFaLib, TwoFALibError, getTwoFaLibVaultCreationUtils, InitializationError, AuthenticationError, EntryNotFoundError, TokenGenerationError, TwoFaLibEvent, };
|
|
12
|
-
export type { Entry, EntryId, NewEntry, EntryMeta, EntryMetaWithToken, EntryType, TotpPayload, Token, EncryptedVaultStateString, LockedRepresentationString, CryptoLib, Encrypted, EncryptedPrivateKey, EncryptedPublicKey, EncryptedSymmetricKey, PrivateKey, SymmetricKey, PublicKey, Passphrase, Salt, DeviceId, DeviceType,
|
|
12
|
+
export type { Entry, EntryId, NewEntry, EntryMeta, EntryMetaWithToken, EntryType, TotpPayload, Token, EncryptedVaultStateString, LockedRepresentationString, CryptoLib, Encrypted, EncryptedPrivateKey, EncryptedPublicKey, EncryptedSymmetricKey, PrivateKey, SymmetricKey, PublicKey, Passphrase, Salt, DeviceId, DeviceType, DeviceFriendlyName, PublicSyncDevice, };
|
|
@@ -49,8 +49,15 @@ class CommandManager {
|
|
|
49
49
|
const command = this.executedCommands.pop();
|
|
50
50
|
if (command) {
|
|
51
51
|
const undoCommand = command.createUndoCommand(this.mediator);
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
// check if the last command was undoable
|
|
53
|
+
if (undoCommand) {
|
|
54
|
+
await undoCommand.execute(this.mediator);
|
|
55
|
+
this.undoneCommands.push(command);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// if it was not, skip it
|
|
59
|
+
await this.undo();
|
|
60
|
+
}
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
/**
|
|
@@ -55,7 +55,8 @@ class PersistentStorageManager {
|
|
|
55
55
|
vault,
|
|
56
56
|
deviceId: this.deviceId,
|
|
57
57
|
sync: {
|
|
58
|
-
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
59
|
+
devices: this.syncManager ? this.syncManager['syncDevices'] : [],
|
|
59
60
|
serverUrl: this.syncManager?.serverUrl,
|
|
60
61
|
commandSendQueue: this.syncManager?.getCommandSendQueue() ?? [],
|
|
61
62
|
},
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { SyncDevice, DeviceId,
|
|
1
|
+
import { SyncDevice, DeviceId, PublicSyncDevice } from '../interfaces/SyncTypes.mjs';
|
|
2
2
|
import type { PrivateKey, PublicKey } from '../interfaces/CryptoLib.mjs';
|
|
3
3
|
import type Command from '../Command/BaseCommand.mjs';
|
|
4
4
|
import type TwoFaLibMediator from '../TwoFaLibMediator.mjs';
|
|
5
5
|
import { VaultSyncStateWithServerUrl } from '../interfaces/Vault.mjs';
|
|
6
6
|
import { SyncCommandFromServer } from 'favaserver/ServerMessage';
|
|
7
7
|
import { SyncCommandFromClient } from 'favaserver/ClientMessage';
|
|
8
|
-
import type { AddSyncDeviceData } from '../Command/commands/AddSyncDeviceCommand.mjs';
|
|
9
8
|
export declare enum ConnectionStatus {
|
|
10
9
|
CONNECTING = 0,
|
|
11
10
|
CONNECTED = 1,
|
|
@@ -17,36 +16,40 @@ export declare enum ConnectionStatus {
|
|
|
17
16
|
*/
|
|
18
17
|
declare class SyncManager {
|
|
19
18
|
private readonly mediator;
|
|
20
|
-
private readonly deviceType;
|
|
21
19
|
private readonly publicKey;
|
|
22
20
|
private readonly privateKey;
|
|
23
21
|
private ws?;
|
|
24
22
|
private activeAddDeviceFlow?;
|
|
25
23
|
private readonly reconnectInterval;
|
|
26
24
|
readonly serverUrl: string;
|
|
27
|
-
syncDevices
|
|
25
|
+
private syncDevices;
|
|
28
26
|
deviceId: DeviceId;
|
|
29
27
|
private readyEventEmitted;
|
|
30
28
|
private commandSendQueue;
|
|
31
29
|
private reconnectTimeout?;
|
|
32
30
|
private terminateTimeout?;
|
|
31
|
+
private connectionFailedTimeout?;
|
|
33
32
|
private shouldReconnect;
|
|
34
33
|
/**
|
|
35
34
|
* Public getter for the command send queue.
|
|
36
35
|
* @returns The command send queue.
|
|
37
36
|
*/
|
|
38
37
|
getCommandSendQueue(): SyncCommandFromClient[];
|
|
38
|
+
/**
|
|
39
|
+
* Public getter for the sync devices
|
|
40
|
+
* @returns The sync devices (without their public key)
|
|
41
|
+
*/
|
|
42
|
+
getSyncDevices(): PublicSyncDevice[];
|
|
39
43
|
/**
|
|
40
44
|
* Creates an instance of SyncManager.
|
|
41
45
|
* @param mediator - The mediator for accessing other components.
|
|
42
|
-
* @param deviceType - The type of the device.
|
|
43
46
|
* @param publicKey - The public key of the device.
|
|
44
47
|
* @param privateKey - The private key of the device.
|
|
45
48
|
* @param syncState - The state of the sync.
|
|
46
49
|
* @param deviceId - The unique identifier of the device.
|
|
47
50
|
* @throws {InitializationError} If initialization fails (e.g., if the server URL is invalid).
|
|
48
51
|
*/
|
|
49
|
-
constructor(mediator: TwoFaLibMediator,
|
|
52
|
+
constructor(mediator: TwoFaLibMediator, publicKey: PublicKey, privateKey: PrivateKey, syncState: VaultSyncStateWithServerUrl, deviceId: DeviceId);
|
|
50
53
|
private get libraryLoader();
|
|
51
54
|
private get cryptoLib();
|
|
52
55
|
private get persistentStorageManager();
|
|
@@ -129,8 +132,9 @@ declare class SyncManager {
|
|
|
129
132
|
respondToAddDeviceFlow(initiatorData: string | Uint8Array | File, initiatorDataType: 'text' | 'qr'): Promise<void>;
|
|
130
133
|
private finishAddDeviceFlowKeyExchangeInitiator;
|
|
131
134
|
private finishAddDeviceFlowKeyExchangeResponder;
|
|
132
|
-
private
|
|
135
|
+
private sendFullVaultData;
|
|
133
136
|
private importInitialVaultState;
|
|
137
|
+
private importVaultState;
|
|
134
138
|
/**
|
|
135
139
|
* Cancels the active add sync device flow.
|
|
136
140
|
* @throws {SyncNoServerConnectionError} If there is no server connection.
|
|
@@ -155,11 +159,20 @@ declare class SyncManager {
|
|
|
155
159
|
* @throws {CryptoError} If decryption fails.
|
|
156
160
|
*/
|
|
157
161
|
receiveCommands(encryptedCommands: SyncCommandFromServer[]): Promise<void>;
|
|
162
|
+
/**
|
|
163
|
+
* Sends vault data to the server for each sync device
|
|
164
|
+
*/
|
|
165
|
+
private resilver;
|
|
158
166
|
/**
|
|
159
167
|
* Add a sync device
|
|
160
168
|
* @param deviceInfo - The info about the device
|
|
169
|
+
* @param saveAfter - Whether to save the new vault after adding it (set to false when adding multiple devices)
|
|
170
|
+
*/
|
|
171
|
+
addSyncDevice(deviceInfo: SyncDevice, saveAfter?: boolean): Promise<void>;
|
|
172
|
+
/**
|
|
173
|
+
* Requests a resilver of the vault
|
|
161
174
|
*/
|
|
162
|
-
|
|
175
|
+
requestResilver(): void;
|
|
163
176
|
/**
|
|
164
177
|
* Function to call when the server connection should be closed
|
|
165
178
|
*/
|
|
@@ -30,19 +30,29 @@ class SyncManager {
|
|
|
30
30
|
getCommandSendQueue() {
|
|
31
31
|
return this.commandSendQueue;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Public getter for the sync devices
|
|
35
|
+
* @returns The sync devices (without their public key)
|
|
36
|
+
*/
|
|
37
|
+
getSyncDevices() {
|
|
38
|
+
return this.syncDevices
|
|
39
|
+
.filter((d) => d.deviceId !== this.deviceId)
|
|
40
|
+
.map((d) => ({
|
|
41
|
+
deviceId: d.deviceId,
|
|
42
|
+
meta: d.meta,
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
33
45
|
/**
|
|
34
46
|
* Creates an instance of SyncManager.
|
|
35
47
|
* @param mediator - The mediator for accessing other components.
|
|
36
|
-
* @param deviceType - The type of the device.
|
|
37
48
|
* @param publicKey - The public key of the device.
|
|
38
49
|
* @param privateKey - The private key of the device.
|
|
39
50
|
* @param syncState - The state of the sync.
|
|
40
51
|
* @param deviceId - The unique identifier of the device.
|
|
41
52
|
* @throws {InitializationError} If initialization fails (e.g., if the server URL is invalid).
|
|
42
53
|
*/
|
|
43
|
-
constructor(mediator,
|
|
54
|
+
constructor(mediator, publicKey, privateKey, syncState, deviceId) {
|
|
44
55
|
this.mediator = mediator;
|
|
45
|
-
this.deviceType = deviceType;
|
|
46
56
|
this.publicKey = publicKey;
|
|
47
57
|
this.privateKey = privateKey;
|
|
48
58
|
this.reconnectInterval = IN_TESTING ? 100 : 5000; // 5 seconds
|
|
@@ -60,8 +70,13 @@ class SyncManager {
|
|
|
60
70
|
this.commandSendQueue = commandSendQueue;
|
|
61
71
|
this.serverUrl = serverUrl;
|
|
62
72
|
this.initServerConnection();
|
|
73
|
+
// add ourselves to the list of syncdevices if we're missing
|
|
74
|
+
void this.addSyncDevice({
|
|
75
|
+
deviceId: deviceId,
|
|
76
|
+
publicKey: this.publicKey,
|
|
77
|
+
}, false);
|
|
63
78
|
// if not yet connected after 2 tries, emit ready event so we can continue
|
|
64
|
-
setTimeout(() => {
|
|
79
|
+
this.connectionFailedTimeout = setTimeout(() => {
|
|
65
80
|
if (!this.readyEventEmitted && !this.webSocketConnected) {
|
|
66
81
|
this.log('warning', 'Failed to connect to sync backend');
|
|
67
82
|
this.dispatchLibEvent(TwoFaLibEvent.Ready);
|
|
@@ -142,6 +157,8 @@ class SyncManager {
|
|
|
142
157
|
this.dispatchLibEvent(TwoFaLibEvent.ConnectionToSyncServerStatusChanged, {
|
|
143
158
|
newStatus: ConnectionStatus.CONNECTED,
|
|
144
159
|
});
|
|
160
|
+
clearTimeout(this.connectionFailedTimeout);
|
|
161
|
+
this.connectionFailedTimeout = undefined;
|
|
145
162
|
// send any commands that were done while offline
|
|
146
163
|
void this.processCommandSendQueue();
|
|
147
164
|
});
|
|
@@ -184,7 +201,7 @@ class SyncManager {
|
|
|
184
201
|
round1Result: jsonToUint8Array(unconvertedPass2Result.round1Result),
|
|
185
202
|
round2Result: jsonToUint8Array(unconvertedPass2Result.round2Result),
|
|
186
203
|
};
|
|
187
|
-
void this.finishAddDeviceFlowKeyExchangeInitiator(pass2Result, data.responderDeviceId
|
|
204
|
+
void this.finishAddDeviceFlowKeyExchangeInitiator(pass2Result, data.responderDeviceId);
|
|
188
205
|
break;
|
|
189
206
|
}
|
|
190
207
|
case 'JPAKEPass3': {
|
|
@@ -196,13 +213,21 @@ class SyncManager {
|
|
|
196
213
|
case 'publicKey': {
|
|
197
214
|
const { data } = message;
|
|
198
215
|
const { responderEncryptedPublicKey } = data;
|
|
199
|
-
void this.
|
|
216
|
+
void this.sendFullVaultData(responderEncryptedPublicKey);
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case 'initialVault': {
|
|
220
|
+
const { data } = message;
|
|
221
|
+
const { encryptedVaultData } = data;
|
|
222
|
+
void this.importInitialVaultState(encryptedVaultData);
|
|
200
223
|
break;
|
|
201
224
|
}
|
|
202
225
|
case 'vault': {
|
|
203
226
|
const { data } = message;
|
|
204
|
-
const { encryptedVaultData,
|
|
205
|
-
void this.
|
|
227
|
+
const { encryptedVaultData, encryptedSymmetricKey, fromDeviceId } = data;
|
|
228
|
+
void this.cryptoLib
|
|
229
|
+
.decrypt(this.privateKey, encryptedSymmetricKey)
|
|
230
|
+
.then((symmetricKey) => this.importVaultState(encryptedVaultData, symmetricKey, fromDeviceId));
|
|
206
231
|
break;
|
|
207
232
|
}
|
|
208
233
|
case 'syncCommandsReceived': {
|
|
@@ -215,6 +240,12 @@ class SyncManager {
|
|
|
215
240
|
void this.receiveCommands(commands);
|
|
216
241
|
break;
|
|
217
242
|
}
|
|
243
|
+
case 'startResilver': {
|
|
244
|
+
// const { data } = message
|
|
245
|
+
// todo: check for missing deviceIds
|
|
246
|
+
void this.resilver();
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
218
249
|
}
|
|
219
250
|
}
|
|
220
251
|
attemptReconnect() {
|
|
@@ -257,7 +288,6 @@ class SyncManager {
|
|
|
257
288
|
});
|
|
258
289
|
// register this add device request at the server
|
|
259
290
|
this.sendToServer('addSyncDeviceInitialiseData', {
|
|
260
|
-
initiatorDeviceType: this.deviceType,
|
|
261
291
|
initiatorDeviceId: this.deviceId,
|
|
262
292
|
timestamp,
|
|
263
293
|
nonce: await this.getNonce(),
|
|
@@ -267,7 +297,6 @@ class SyncManager {
|
|
|
267
297
|
const returnData = {
|
|
268
298
|
addDevicePassword: uint8ArrayToBase64(addDevicePassword),
|
|
269
299
|
initiatorDeviceId: this.deviceId,
|
|
270
|
-
initiatorDeviceType: this.deviceType,
|
|
271
300
|
timestamp,
|
|
272
301
|
pass1Result: {
|
|
273
302
|
G1: uint8ArrayToHex(pass1Result.G1),
|
|
@@ -304,12 +333,11 @@ class SyncManager {
|
|
|
304
333
|
if (this.activeAddDeviceFlow) {
|
|
305
334
|
throw new SyncAddDeviceFlowConflictError();
|
|
306
335
|
}
|
|
307
|
-
const { addDevicePassword, initiatorDeviceId, timestamp, pass1Result
|
|
336
|
+
const { addDevicePassword, initiatorDeviceId, timestamp, pass1Result } = await decodeInitiatorData(initiatorData, initiatorDataType, await this.libraryLoader.getJsQrLib(), this.libraryLoader.getCanvasLib.bind(this));
|
|
308
337
|
if (!addDevicePassword ||
|
|
309
338
|
!initiatorDeviceId ||
|
|
310
339
|
!timestamp ||
|
|
311
|
-
!pass1Result
|
|
312
|
-
!initiatorDeviceIdentifier) {
|
|
340
|
+
!pass1Result) {
|
|
313
341
|
throw new SyncError('Missing required fields in initiator data');
|
|
314
342
|
}
|
|
315
343
|
// Decode the base64 password
|
|
@@ -335,7 +363,6 @@ class SyncManager {
|
|
|
335
363
|
addDevicePassword: decodedPassword,
|
|
336
364
|
responderDeviceId: this.deviceId,
|
|
337
365
|
initiatorDeviceId: initiatorDeviceId,
|
|
338
|
-
initiatorDeviceType: initiatorDeviceIdentifier,
|
|
339
366
|
timestamp: Date.now(),
|
|
340
367
|
};
|
|
341
368
|
// respond to this add device request at the server
|
|
@@ -345,10 +372,9 @@ class SyncManager {
|
|
|
345
372
|
pass2Result,
|
|
346
373
|
responderDeviceId: this.deviceId,
|
|
347
374
|
initiatorDeviceId: initiatorDeviceId,
|
|
348
|
-
responderDeviceType: this.deviceType,
|
|
349
375
|
});
|
|
350
376
|
}
|
|
351
|
-
async finishAddDeviceFlowKeyExchangeInitiator(pass2Result, responderDeviceId
|
|
377
|
+
async finishAddDeviceFlowKeyExchangeInitiator(pass2Result, responderDeviceId) {
|
|
352
378
|
if (!this.ws || !this.webSocketConnected) {
|
|
353
379
|
throw new SyncNoServerConnectionError();
|
|
354
380
|
}
|
|
@@ -368,7 +394,6 @@ class SyncManager {
|
|
|
368
394
|
...this.activeAddDeviceFlow,
|
|
369
395
|
state: 'initiator:syncKeyCreated',
|
|
370
396
|
responderDeviceId: responderDeviceId,
|
|
371
|
-
responderDeviceType: responderDeviceType,
|
|
372
397
|
syncKey,
|
|
373
398
|
};
|
|
374
399
|
}
|
|
@@ -398,7 +423,7 @@ class SyncManager {
|
|
|
398
423
|
initiatorDeviceId: this.activeAddDeviceFlow.initiatorDeviceId,
|
|
399
424
|
});
|
|
400
425
|
}
|
|
401
|
-
async
|
|
426
|
+
async sendFullVaultData(responderEncryptedPublicKey) {
|
|
402
427
|
if (!this.ws || !this.webSocketConnected) {
|
|
403
428
|
throw new SyncNoServerConnectionError();
|
|
404
429
|
}
|
|
@@ -413,51 +438,46 @@ class SyncManager {
|
|
|
413
438
|
const decryptedPublicKey = await this.cryptoLib.decryptSymmetric(syncKey, responderEncryptedPublicKey);
|
|
414
439
|
// get the vault data (encrypted with the sync key)
|
|
415
440
|
const encryptedVaultData = await this.persistentStorageManager.getEncryptedVaultState(syncKey);
|
|
416
|
-
const initiatorEncryptedPublicKey = await this.cryptoLib.encryptSymmetric(syncKey, this.publicKey);
|
|
417
441
|
// Send the encrypted vault data to the server
|
|
418
|
-
this.sendToServer('
|
|
442
|
+
this.sendToServer('initialVault', {
|
|
419
443
|
nonce: await this.getNonce(),
|
|
420
444
|
encryptedVaultData,
|
|
421
445
|
initiatorDeviceId: this.activeAddDeviceFlow.initiatorDeviceId,
|
|
422
|
-
initiatorEncryptedPublicKey,
|
|
423
446
|
});
|
|
424
|
-
// save the added the sync device, done via command so this is synced to
|
|
425
|
-
//
|
|
447
|
+
// save the added the sync device, done via command so this is synced to all sync devices
|
|
448
|
+
// this also re-adds the sync device to the just added sync device, which syncDevices list is now equal to our own
|
|
426
449
|
const command = AddSyncDeviceCommand.create({
|
|
427
450
|
deviceId: this.activeAddDeviceFlow.responderDeviceId,
|
|
428
|
-
deviceType: this.activeAddDeviceFlow.responderDeviceType,
|
|
429
451
|
publicKey: decryptedPublicKey,
|
|
430
452
|
});
|
|
431
453
|
await this.commandManager.execute(command);
|
|
432
454
|
// all done
|
|
433
455
|
this.activeAddDeviceFlow = undefined;
|
|
434
456
|
}
|
|
435
|
-
async importInitialVaultState(encryptedVaultState
|
|
457
|
+
async importInitialVaultState(encryptedVaultState) {
|
|
436
458
|
if (this.activeAddDeviceFlow?.state !== 'responder:syncKeyCreated') {
|
|
437
459
|
throw new SyncInWrongStateError(`Expected responder:syncKeyCreated, got ${this.activeAddDeviceFlow?.state}`);
|
|
438
460
|
}
|
|
439
|
-
|
|
440
|
-
// Decrypt the received public key
|
|
441
|
-
const decryptedPublicKey = await this.cryptoLib.decryptSymmetric(syncKey, encryptedPublicKey);
|
|
442
|
-
const vaultState = JSON.parse(await this.cryptoLib.decryptSymmetric(syncKey, encryptedVaultState));
|
|
443
|
-
if (vaultState.deviceId !== this.activeAddDeviceFlow.initiatorDeviceId) {
|
|
444
|
-
throw new SyncError(`DeviceId mismatch when importing, expected ${this.activeAddDeviceFlow.initiatorDeviceId} got ${vaultState.deviceId}`);
|
|
445
|
-
}
|
|
446
|
-
this.syncDevices = vaultState.sync.devices;
|
|
447
|
-
this.mediator
|
|
448
|
-
.getComponent('vaultDataManager')
|
|
449
|
-
.replaceVault(vaultState.vault);
|
|
450
|
-
// Update the sync devices list with the initiator's information
|
|
451
|
-
// Not done as a command as all other devices already have the senders info
|
|
452
|
-
await this.addSyncDevice({
|
|
453
|
-
deviceId: this.activeAddDeviceFlow.initiatorDeviceId,
|
|
454
|
-
deviceType: this.activeAddDeviceFlow.initiatorDeviceType,
|
|
455
|
-
publicKey: decryptedPublicKey,
|
|
456
|
-
});
|
|
461
|
+
await this.importVaultState(encryptedVaultState, this.activeAddDeviceFlow.syncKey, this.activeAddDeviceFlow.initiatorDeviceId);
|
|
457
462
|
// Reset the active add device flow
|
|
458
463
|
this.activeAddDeviceFlow = undefined;
|
|
459
464
|
this.dispatchLibEvent(TwoFaLibEvent.ConnectToExistingVaultFinished);
|
|
460
465
|
}
|
|
466
|
+
async importVaultState(encryptedVaultState, symmetricKey, expectedDeviceId) {
|
|
467
|
+
const vaultState = JSON.parse(await this.cryptoLib.decryptSymmetric(symmetricKey, encryptedVaultState));
|
|
468
|
+
if (vaultState.deviceId !== expectedDeviceId) {
|
|
469
|
+
throw new SyncError(`DeviceId mismatch when importing, expected ${expectedDeviceId} got ${vaultState.deviceId}`);
|
|
470
|
+
}
|
|
471
|
+
this.syncDevices = vaultState.sync.devices;
|
|
472
|
+
for (const device of vaultState.sync.devices) {
|
|
473
|
+
await this.addSyncDevice(device, false);
|
|
474
|
+
}
|
|
475
|
+
const vaultDataManager = this.mediator.getComponent('vaultDataManager');
|
|
476
|
+
for (const entry of vaultState.vault) {
|
|
477
|
+
await vaultDataManager.addEntry(entry, false);
|
|
478
|
+
}
|
|
479
|
+
await this.persistentStorageManager.save();
|
|
480
|
+
}
|
|
461
481
|
/**
|
|
462
482
|
* Cancels the active add sync device flow.
|
|
463
483
|
* @throws {SyncNoServerConnectionError} If there is no server connection.
|
|
@@ -485,6 +505,10 @@ class SyncManager {
|
|
|
485
505
|
async sendCommand(command) {
|
|
486
506
|
const commandJson = command.toJSON();
|
|
487
507
|
await Promise.all(this.syncDevices.map(async (device) => {
|
|
508
|
+
if (device.deviceId === this.deviceId) {
|
|
509
|
+
// skip ourselves
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
488
512
|
const symmetricKey = await this.cryptoLib.createSymmetricKey();
|
|
489
513
|
const encryptedSymmetricKey = await this.cryptoLib.encrypt(device.publicKey, symmetricKey);
|
|
490
514
|
const encryptedCommand = await this.cryptoLib.encryptSymmetric(symmetricKey, JSON.stringify({
|
|
@@ -556,20 +580,50 @@ class SyncManager {
|
|
|
556
580
|
});
|
|
557
581
|
}
|
|
558
582
|
}
|
|
583
|
+
/**
|
|
584
|
+
* Sends vault data to the server for each sync device
|
|
585
|
+
*/
|
|
586
|
+
async resilver() {
|
|
587
|
+
for (const device of this.syncDevices) {
|
|
588
|
+
if (device.deviceId === this.deviceId) {
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
const symmetricKey = await this.cryptoLib.createSymmetricKey();
|
|
592
|
+
const encryptedSymmetricKey = await this.cryptoLib.encrypt(device.publicKey, symmetricKey);
|
|
593
|
+
const encryptedVaultData = await this.persistentStorageManager.getEncryptedVaultState(symmetricKey);
|
|
594
|
+
this.sendToServer('vault', {
|
|
595
|
+
forDeviceId: device.deviceId,
|
|
596
|
+
nonce: await this.getNonce(),
|
|
597
|
+
encryptedVaultData,
|
|
598
|
+
encryptedSymmetricKey,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
}
|
|
559
602
|
/**
|
|
560
603
|
* Add a sync device
|
|
561
604
|
* @param deviceInfo - The info about the device
|
|
605
|
+
* @param saveAfter - Whether to save the new vault after adding it (set to false when adding multiple devices)
|
|
562
606
|
*/
|
|
563
|
-
async addSyncDevice(deviceInfo) {
|
|
564
|
-
if (
|
|
565
|
-
//
|
|
566
|
-
// besides the sender after the device add flow. We don't want to
|
|
567
|
-
// add ourselves to our syncDevices
|
|
607
|
+
async addSyncDevice(deviceInfo, saveAfter = true) {
|
|
608
|
+
if (this.syncDevices.some((d) => d.deviceId === deviceInfo.deviceId)) {
|
|
609
|
+
// we already have this device
|
|
568
610
|
return;
|
|
569
611
|
}
|
|
570
612
|
this.log('info', `Adding syncdevice ${deviceInfo.deviceId} to ${this.deviceId}`);
|
|
571
|
-
this.syncDevices.push(
|
|
572
|
-
|
|
613
|
+
this.syncDevices.push({
|
|
614
|
+
...deviceInfo,
|
|
615
|
+
});
|
|
616
|
+
if (saveAfter) {
|
|
617
|
+
await this.persistentStorageManager.save();
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Requests a resilver of the vault
|
|
622
|
+
*/
|
|
623
|
+
requestResilver() {
|
|
624
|
+
this.sendToServer('startResilver', {
|
|
625
|
+
deviceIds: this.syncDevices.map((d) => d.deviceId),
|
|
626
|
+
});
|
|
573
627
|
}
|
|
574
628
|
/**
|
|
575
629
|
* Function to call when the server connection should be closed
|
|
@@ -42,10 +42,11 @@ declare class VaultDataManager {
|
|
|
42
42
|
generateTokenForEntry(id: EntryId, timestamp?: number): Token;
|
|
43
43
|
/**
|
|
44
44
|
* Add a new entry to the vault.
|
|
45
|
-
* @param entry - The entry data to add
|
|
45
|
+
* @param entry - The entry data to add
|
|
46
|
+
* @param saveAfter - Whether to save the new vault after adding it (set to false when adding multiple entries)
|
|
46
47
|
* @returns A promise that resolves when the entry is added.
|
|
47
48
|
*/
|
|
48
|
-
addEntry(entry: Entry): Promise<void>;
|
|
49
|
+
addEntry(entry: Entry, saveAfter?: boolean): Promise<void>;
|
|
49
50
|
/**
|
|
50
51
|
* Delete an entry from the vault.
|
|
51
52
|
* @param entryId - The identifier of the entry to delete.
|
|
@@ -70,12 +70,19 @@ class VaultDataManager {
|
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Add a new entry to the vault.
|
|
73
|
-
* @param entry - The entry data to add
|
|
73
|
+
* @param entry - The entry data to add
|
|
74
|
+
* @param saveAfter - Whether to save the new vault after adding it (set to false when adding multiple entries)
|
|
74
75
|
* @returns A promise that resolves when the entry is added.
|
|
75
76
|
*/
|
|
76
|
-
async addEntry(entry) {
|
|
77
|
+
async addEntry(entry, saveAfter = true) {
|
|
78
|
+
if (this.vault.find((e) => e.id === entry.id)) {
|
|
79
|
+
// We already have this entry
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
77
82
|
this.vault.push(entry);
|
|
78
|
-
|
|
83
|
+
if (saveAfter) {
|
|
84
|
+
await this.persistentStorageManager.save();
|
|
85
|
+
}
|
|
79
86
|
}
|
|
80
87
|
/**
|
|
81
88
|
* Delete an entry from the vault.
|