favalib 0.0.1
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 +65 -0
- package/build/Command/BaseCommand.mjs +54 -0
- package/build/Command/CommandQueue.d.mts +28 -0
- package/build/Command/CommandQueue.mjs +43 -0
- package/build/Command/commandConstructors.d.mts +11 -0
- package/build/Command/commandConstructors.mjs +11 -0
- package/build/Command/commands/AddEntryCommand.d.mts +31 -0
- package/build/Command/commands/AddEntryCommand.mjs +43 -0
- package/build/Command/commands/AddSyncDeviceCommand.d.mts +36 -0
- package/build/Command/commands/AddSyncDeviceCommand.mjs +42 -0
- package/build/Command/commands/DeleteEntryCommand.d.mts +35 -0
- package/build/Command/commands/DeleteEntryCommand.mjs +50 -0
- package/build/Command/commands/UpdateEntryCommand.d.mts +38 -0
- package/build/Command/commands/UpdateEntryCommand.mjs +51 -0
- package/build/CryptoProviders/browser/index.d.mts +73 -0
- package/build/CryptoProviders/browser/index.mjs +209 -0
- package/build/CryptoProviders/node/index.d.mts +62 -0
- package/build/CryptoProviders/node/index.mjs +189 -0
- package/build/TwoFALibError.d.mts +77 -0
- package/build/TwoFALibError.mjs +91 -0
- package/build/TwoFaLib.d.mts +95 -0
- package/build/TwoFaLib.mjs +180 -0
- package/build/TwoFaLibEvent.d.mts +8 -0
- package/build/TwoFaLibEvent.mjs +9 -0
- package/build/TwoFaLibMediator.d.mts +37 -0
- package/build/TwoFaLibMediator.mjs +58 -0
- package/build/interfaces/CommandTypes.d.mts +20 -0
- package/build/interfaces/CommandTypes.mjs +1 -0
- package/build/interfaces/CryptoLib.d.mts +113 -0
- package/build/interfaces/CryptoLib.mjs +1 -0
- package/build/interfaces/Entry.d.mts +33 -0
- package/build/interfaces/Entry.mjs +1 -0
- package/build/interfaces/Events.d.mts +22 -0
- package/build/interfaces/Events.mjs +1 -0
- package/build/interfaces/PassphraseExtraDict.d.ts +2 -0
- package/build/interfaces/PassphraseExtraDict.js +1 -0
- package/build/interfaces/SyncTypes.d.mts +45 -0
- package/build/interfaces/SyncTypes.mjs +1 -0
- package/build/interfaces/Vault.d.mts +30 -0
- package/build/interfaces/Vault.mjs +1 -0
- package/build/main.d.mts +12 -0
- package/build/main.mjs +5 -0
- package/build/subclasses/CommandManager.d.mts +46 -0
- package/build/subclasses/CommandManager.mjs +117 -0
- package/build/subclasses/ExportImportManager.d.mts +58 -0
- package/build/subclasses/ExportImportManager.mjs +105 -0
- package/build/subclasses/LibraryLoader.d.mts +56 -0
- package/build/subclasses/LibraryLoader.mjs +108 -0
- package/build/subclasses/PersistentStorageManager.d.mts +71 -0
- package/build/subclasses/PersistentStorageManager.mjs +127 -0
- package/build/subclasses/SyncManager.d.mts +161 -0
- package/build/subclasses/SyncManager.mjs +567 -0
- package/build/subclasses/VaultDataManager.d.mts +68 -0
- package/build/subclasses/VaultDataManager.mjs +114 -0
- package/build/subclasses/VaultOperationsManager.d.mts +91 -0
- package/build/subclasses/VaultOperationsManager.mjs +163 -0
- package/build/utils/constants.d.mts +2 -0
- package/build/utils/constants.mjs +1 -0
- package/build/utils/creationUtils.d.mts +43 -0
- package/build/utils/creationUtils.mjs +125 -0
- package/build/utils/exportImportUtils.d.mts +53 -0
- package/build/utils/exportImportUtils.mjs +185 -0
- package/build/utils/qrUtils.d.mts +25 -0
- package/build/utils/qrUtils.mjs +84 -0
- package/build/utils/syncUtils.d.mts +26 -0
- package/build/utils/syncUtils.mjs +78 -0
- package/package.json +56 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Tagged } from 'type-fest';
|
|
2
|
+
import type { Encrypted, EncryptedPrivateKey, EncryptedSymmetricKey, Salt } from './CryptoLib.mjs';
|
|
3
|
+
import type Entry from './Entry.mjs';
|
|
4
|
+
import { DeviceId, SyncDevice } from './SyncTypes.mjs';
|
|
5
|
+
import { SyncCommandFromClient } from 'favaserver/ClientMessage';
|
|
6
|
+
export type Vault = Entry[];
|
|
7
|
+
export type EncryptedVaultStateString = Encrypted<VaultStateString>;
|
|
8
|
+
export interface LockedRepresentation {
|
|
9
|
+
encryptedPrivateKey: EncryptedPrivateKey;
|
|
10
|
+
encryptedSymmetricKey: EncryptedSymmetricKey;
|
|
11
|
+
salt: Salt;
|
|
12
|
+
encryptedVaultState: EncryptedVaultStateString;
|
|
13
|
+
libVersion: string;
|
|
14
|
+
storageVersion: number;
|
|
15
|
+
}
|
|
16
|
+
export type LockedRepresentationString = Tagged<string, 'LockedRepresentationString'>;
|
|
17
|
+
export interface VaultSyncState {
|
|
18
|
+
devices: SyncDevice[];
|
|
19
|
+
serverUrl: string | undefined;
|
|
20
|
+
commandSendQueue: SyncCommandFromClient[];
|
|
21
|
+
}
|
|
22
|
+
export type VaultSyncStateWithServerUrl = Omit<VaultSyncState, 'serverUrl'> & {
|
|
23
|
+
serverUrl: NonNullable<VaultSyncState['serverUrl']>;
|
|
24
|
+
};
|
|
25
|
+
export interface VaultState {
|
|
26
|
+
deviceId: DeviceId;
|
|
27
|
+
vault: Vault;
|
|
28
|
+
sync: VaultSyncState;
|
|
29
|
+
}
|
|
30
|
+
export type VaultStateString = Tagged<string, 'VaultState'>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/build/main.d.mts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import TwoFaLib from './TwoFaLib.mjs';
|
|
2
|
+
import type Entry from './interfaces/Entry.mjs';
|
|
3
|
+
import type { EntryId, NewEntry, EntryMeta, EntryType, TotpPayload, Token, EntryMetaWithToken } from './interfaces/Entry.mjs';
|
|
4
|
+
import type CryptoLib from './interfaces/CryptoLib.mjs';
|
|
5
|
+
import type { Encrypted, EncryptedPrivateKey, EncryptedSymmetricKey, EncryptedPublicKey, PrivateKey, SymmetricKey, PublicKey, Passphrase, Salt } from './interfaces/CryptoLib.mjs';
|
|
6
|
+
import type { SyncDevice, DeviceId, DeviceType } from './interfaces/SyncTypes.mjs';
|
|
7
|
+
import type { EncryptedVaultStateString, LockedRepresentationString } from './interfaces/Vault.mjs';
|
|
8
|
+
import { TwoFALibError, InitializationError, AuthenticationError, EntryNotFoundError, TokenGenerationError } from './TwoFALibError.mjs';
|
|
9
|
+
import { TwoFaLibEvent } from './TwoFaLibEvent.mjs';
|
|
10
|
+
import { getTwoFaLibVaultCreationUtils } from './utils/creationUtils.mjs';
|
|
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, SyncDevice, };
|
package/build/main.mjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import TwoFaLib from './TwoFaLib.mjs';
|
|
2
|
+
import { TwoFALibError, InitializationError, AuthenticationError, EntryNotFoundError, TokenGenerationError, } from './TwoFALibError.mjs';
|
|
3
|
+
import { TwoFaLibEvent } from './TwoFaLibEvent.mjs';
|
|
4
|
+
import { getTwoFaLibVaultCreationUtils } from './utils/creationUtils.mjs';
|
|
5
|
+
export { TwoFaLib, TwoFALibError, getTwoFaLibVaultCreationUtils, InitializationError, AuthenticationError, EntryNotFoundError, TokenGenerationError, TwoFaLibEvent, };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type TwoFaLibMediator from '../TwoFaLibMediator.mjs';
|
|
2
|
+
import type Command from '../Command/BaseCommand.mjs';
|
|
3
|
+
import { type SyncCommand } from '../interfaces/CommandTypes.mjs';
|
|
4
|
+
/**
|
|
5
|
+
* Manages the execution, undo, and redo of commands.
|
|
6
|
+
*/
|
|
7
|
+
declare class CommandManager {
|
|
8
|
+
private readonly mediator;
|
|
9
|
+
private executedCommands;
|
|
10
|
+
private undoneCommands;
|
|
11
|
+
private remoteCommandQueue;
|
|
12
|
+
private processedCommandIds;
|
|
13
|
+
/**
|
|
14
|
+
* Constructs a new CommandManager instance.
|
|
15
|
+
* @param mediator - The mediator for accessing other components.
|
|
16
|
+
*/
|
|
17
|
+
constructor(mediator: TwoFaLibMediator);
|
|
18
|
+
private get syncManager();
|
|
19
|
+
private get log();
|
|
20
|
+
/**
|
|
21
|
+
* Executes a command and manages its state.
|
|
22
|
+
* @param command - The command to execute.
|
|
23
|
+
*/
|
|
24
|
+
execute(command: Command): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Undoes the last executed command.
|
|
27
|
+
*/
|
|
28
|
+
undo(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Redoes the last undone command.
|
|
31
|
+
*/
|
|
32
|
+
redo(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Processes all commands in the remote command queue.
|
|
35
|
+
* @returns An array of the IDs of the succesfully executed commands.
|
|
36
|
+
*/
|
|
37
|
+
processRemoteCommands(): Promise<string[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Receives a remote command and enqueues it for processing.
|
|
40
|
+
* @param remoteCommand - The remote command to process.
|
|
41
|
+
* @throws {InvalidCommandError} If the command type is unknown or data is invalid.
|
|
42
|
+
*/
|
|
43
|
+
receiveRemoteCommand(remoteCommand: SyncCommand): void;
|
|
44
|
+
private sendCommandToOtherInstances;
|
|
45
|
+
}
|
|
46
|
+
export default CommandManager;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { InvalidCommandError } from '../TwoFALibError.mjs';
|
|
2
|
+
import commandConstructors from '../Command/commandConstructors.mjs';
|
|
3
|
+
import CommandQueue from '../Command/CommandQueue.mjs';
|
|
4
|
+
/**
|
|
5
|
+
* Manages the execution, undo, and redo of commands.
|
|
6
|
+
*/
|
|
7
|
+
class CommandManager {
|
|
8
|
+
/**
|
|
9
|
+
* Constructs a new CommandManager instance.
|
|
10
|
+
* @param mediator - The mediator for accessing other components.
|
|
11
|
+
*/
|
|
12
|
+
constructor(mediator) {
|
|
13
|
+
this.mediator = mediator;
|
|
14
|
+
this.executedCommands = [];
|
|
15
|
+
this.undoneCommands = [];
|
|
16
|
+
this.remoteCommandQueue = new CommandQueue();
|
|
17
|
+
this.processedCommandIds = new Set();
|
|
18
|
+
}
|
|
19
|
+
get syncManager() {
|
|
20
|
+
if (!this.mediator.componentIsInitialised('syncManager')) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return this.mediator.getComponent('syncManager');
|
|
24
|
+
}
|
|
25
|
+
get log() {
|
|
26
|
+
return this.mediator.getComponent('log');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Executes a command and manages its state.
|
|
30
|
+
* @param command - The command to execute.
|
|
31
|
+
*/
|
|
32
|
+
async execute(command) {
|
|
33
|
+
if (this.processedCommandIds.has(command.id)) {
|
|
34
|
+
this.log('warning', `Command ${command.id} has already been processed`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
await command.execute(this.mediator);
|
|
38
|
+
this.processedCommandIds.add(command.id);
|
|
39
|
+
if (!command.fromRemote) {
|
|
40
|
+
this.executedCommands.push(command);
|
|
41
|
+
await this.sendCommandToOtherInstances(command);
|
|
42
|
+
}
|
|
43
|
+
this.undoneCommands = [];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Undoes the last executed command.
|
|
47
|
+
*/
|
|
48
|
+
async undo() {
|
|
49
|
+
const command = this.executedCommands.pop();
|
|
50
|
+
if (command) {
|
|
51
|
+
const undoCommand = command.createUndoCommand(this.mediator);
|
|
52
|
+
await undoCommand.execute(this.mediator);
|
|
53
|
+
this.undoneCommands.push(command);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Redoes the last undone command.
|
|
58
|
+
*/
|
|
59
|
+
async redo() {
|
|
60
|
+
const command = this.undoneCommands.pop();
|
|
61
|
+
if (command) {
|
|
62
|
+
await command.execute(this.mediator);
|
|
63
|
+
this.executedCommands.push(command);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Processes all commands in the remote command queue.
|
|
68
|
+
* @returns An array of the IDs of the succesfully executed commands.
|
|
69
|
+
*/
|
|
70
|
+
async processRemoteCommands() {
|
|
71
|
+
const executedIds = [];
|
|
72
|
+
while (!this.remoteCommandQueue.isEmpty()) {
|
|
73
|
+
const command = this.remoteCommandQueue.dequeue();
|
|
74
|
+
if (command) {
|
|
75
|
+
try {
|
|
76
|
+
await this.execute(command);
|
|
77
|
+
executedIds.push(command.id);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
// eslint-disable-next-line no-restricted-globals
|
|
81
|
+
if (err instanceof Error) {
|
|
82
|
+
this.log('warning', 'Error while processing remote commands: ' + err.message);
|
|
83
|
+
}
|
|
84
|
+
this.log('warning', 'Unknown error while processing remote commands');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return executedIds;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Receives a remote command and enqueues it for processing.
|
|
92
|
+
* @param remoteCommand - The remote command to process.
|
|
93
|
+
* @throws {InvalidCommandError} If the command type is unknown or data is invalid.
|
|
94
|
+
*/
|
|
95
|
+
receiveRemoteCommand(remoteCommand) {
|
|
96
|
+
if (remoteCommand && typeof remoteCommand.type === 'string') {
|
|
97
|
+
const CommandClass = commandConstructors[remoteCommand.type];
|
|
98
|
+
if (CommandClass) {
|
|
99
|
+
const command = CommandClass.fromJSON(remoteCommand);
|
|
100
|
+
this.remoteCommandQueue.enqueue(command);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
throw new InvalidCommandError(`Unknown command type: ${remoteCommand.type}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
throw new InvalidCommandError('Invalid command data received');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async sendCommandToOtherInstances(command) {
|
|
111
|
+
if (!this.syncManager) {
|
|
112
|
+
return Promise.resolve();
|
|
113
|
+
}
|
|
114
|
+
await this.syncManager.sendCommand(command);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export default CommandManager;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { EntryId } from '../interfaces/Entry.mjs';
|
|
2
|
+
import type { PassphraseExtraDict } from '../interfaces/PassphraseExtraDict.js';
|
|
3
|
+
import type TwoFaLibMediator from '../TwoFaLibMediator.mjs';
|
|
4
|
+
/**
|
|
5
|
+
* Manages the export and import of entries in various formats.
|
|
6
|
+
*/
|
|
7
|
+
declare class ExportImportManager {
|
|
8
|
+
private readonly mediator;
|
|
9
|
+
private readonly passphraseExtraDict;
|
|
10
|
+
/**
|
|
11
|
+
* Constructs a new instance of ExportImportManager.
|
|
12
|
+
* @param mediator - The mediator for accessing other components.
|
|
13
|
+
* @param passphraseExtraDict - Additional words to be used for passphrase strength evaluation.
|
|
14
|
+
*/
|
|
15
|
+
constructor(mediator: TwoFaLibMediator, passphraseExtraDict: PassphraseExtraDict);
|
|
16
|
+
private get libraryLoader();
|
|
17
|
+
private get vaultDataManager();
|
|
18
|
+
private get vaultOperationsManager();
|
|
19
|
+
private get persistentStorageManager();
|
|
20
|
+
/**
|
|
21
|
+
* Export entries in the specified format, optionally (when a password is provided) encrypted.
|
|
22
|
+
* If the password is not provided, the user be warned about the dangers of exporting clear text.
|
|
23
|
+
* @param format - The export format ('text' or 'html').
|
|
24
|
+
* @param passphrase - Optional password for encryption.
|
|
25
|
+
* @param userWasWarnedAboutExportingClearText - Whether the user was warned about exporting clear text.
|
|
26
|
+
* @returns A promise that resolves to the exported data as a string.
|
|
27
|
+
*/
|
|
28
|
+
exportEntries(format: 'text' | 'html', passphrase?: string, userWasWarnedAboutExportingClearText?: boolean): Promise<string>;
|
|
29
|
+
private generateTextExport;
|
|
30
|
+
private generateHtmlExport;
|
|
31
|
+
/**
|
|
32
|
+
* Import entries from a text file, optionally (when a password is provided) decrypt first
|
|
33
|
+
* @param fileContents - The contents of the text file.
|
|
34
|
+
* @param passphrase - Optional password for decryption.
|
|
35
|
+
* @returns A promise that resolves to an array of objects, each containing the line number,
|
|
36
|
+
* the EntryId or null if it was not a valid entry and the error if there was one.
|
|
37
|
+
*/
|
|
38
|
+
importFromTextFile(fileContents: string, passphrase?: string): Promise<{
|
|
39
|
+
lineNr: number;
|
|
40
|
+
entryId: EntryId | null;
|
|
41
|
+
error: unknown;
|
|
42
|
+
}[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Import an entry from an OTP URI.
|
|
45
|
+
* @param otpUri - The OTP URI to import
|
|
46
|
+
* @returns A promise that resolves to the newly added EntryId.
|
|
47
|
+
* @throws {ExportImportError} If the URI is invalid or contains unsupported OTP type.
|
|
48
|
+
*/
|
|
49
|
+
importFromUri(otpUri: string): Promise<EntryId>;
|
|
50
|
+
/**
|
|
51
|
+
* Import an entry from a QR code image.
|
|
52
|
+
* @param imageInput - The image containing the QR code
|
|
53
|
+
* @returns A promise that resolves to the newly added EntryId.
|
|
54
|
+
* @throws {InvalidInputExportImportError} If the QR code is invalid or doesn't contain a valid OTP URI.
|
|
55
|
+
*/
|
|
56
|
+
importFromQRCode(imageInput: string | File | Uint8Array): Promise<EntryId>;
|
|
57
|
+
}
|
|
58
|
+
export default ExportImportManager;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { parseOtpUri, generateHtmlExport, generateTextExport, processImportLines, encryptExport, decryptExport, } from '../utils/exportImportUtils.mjs';
|
|
2
|
+
import { getDataFromQRImage } from '../utils/qrUtils.mjs';
|
|
3
|
+
import { ExportImportError } from '../TwoFALibError.mjs';
|
|
4
|
+
import { validatePassphraseStrength } from '../utils/creationUtils.mjs';
|
|
5
|
+
/**
|
|
6
|
+
* Manages the export and import of entries in various formats.
|
|
7
|
+
*/
|
|
8
|
+
class ExportImportManager {
|
|
9
|
+
/**
|
|
10
|
+
* Constructs a new instance of ExportImportManager.
|
|
11
|
+
* @param mediator - The mediator for accessing other components.
|
|
12
|
+
* @param passphraseExtraDict - Additional words to be used for passphrase strength evaluation.
|
|
13
|
+
*/
|
|
14
|
+
constructor(mediator, passphraseExtraDict) {
|
|
15
|
+
this.mediator = mediator;
|
|
16
|
+
this.passphraseExtraDict = passphraseExtraDict;
|
|
17
|
+
}
|
|
18
|
+
get libraryLoader() {
|
|
19
|
+
return this.mediator.getComponent('libraryLoader');
|
|
20
|
+
}
|
|
21
|
+
get vaultDataManager() {
|
|
22
|
+
return this.mediator.getComponent('vaultDataManager');
|
|
23
|
+
}
|
|
24
|
+
get vaultOperationsManager() {
|
|
25
|
+
return this.mediator.getComponent('vaultOperationsManager');
|
|
26
|
+
}
|
|
27
|
+
get persistentStorageManager() {
|
|
28
|
+
return this.mediator.getComponent('persistentStorageManager');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Export entries in the specified format, optionally (when a password is provided) encrypted.
|
|
32
|
+
* If the password is not provided, the user be warned about the dangers of exporting clear text.
|
|
33
|
+
* @param format - The export format ('text' or 'html').
|
|
34
|
+
* @param passphrase - Optional password for encryption.
|
|
35
|
+
* @param userWasWarnedAboutExportingClearText - Whether the user was warned about exporting clear text.
|
|
36
|
+
* @returns A promise that resolves to the exported data as a string.
|
|
37
|
+
*/
|
|
38
|
+
async exportEntries(format, passphrase, userWasWarnedAboutExportingClearText) {
|
|
39
|
+
let exportData;
|
|
40
|
+
if (!passphrase && !userWasWarnedAboutExportingClearText) {
|
|
41
|
+
throw new ExportImportError('User was not warned about the dangers of unencrypted exporting');
|
|
42
|
+
}
|
|
43
|
+
if (format === 'text') {
|
|
44
|
+
exportData = this.generateTextExport();
|
|
45
|
+
}
|
|
46
|
+
else if (format === 'html') {
|
|
47
|
+
exportData = await this.generateHtmlExport();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
throw new ExportImportError('Invalid export format');
|
|
51
|
+
}
|
|
52
|
+
if (passphrase) {
|
|
53
|
+
await validatePassphraseStrength(this.libraryLoader, passphrase, this.passphraseExtraDict);
|
|
54
|
+
return encryptExport(await this.libraryLoader.getOpenPGPLib(), exportData, passphrase);
|
|
55
|
+
}
|
|
56
|
+
return exportData;
|
|
57
|
+
}
|
|
58
|
+
generateTextExport() {
|
|
59
|
+
return generateTextExport(this.vaultDataManager.getAllEntries());
|
|
60
|
+
}
|
|
61
|
+
async generateHtmlExport() {
|
|
62
|
+
const qrGeneratorLib = await this.libraryLoader.getQrGeneratorLib();
|
|
63
|
+
return generateHtmlExport(qrGeneratorLib, this.vaultDataManager.getAllEntries());
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Import entries from a text file, optionally (when a password is provided) decrypt first
|
|
67
|
+
* @param fileContents - The contents of the text file.
|
|
68
|
+
* @param passphrase - Optional password for decryption.
|
|
69
|
+
* @returns A promise that resolves to an array of objects, each containing the line number,
|
|
70
|
+
* the EntryId or null if it was not a valid entry and the error if there was one.
|
|
71
|
+
*/
|
|
72
|
+
async importFromTextFile(fileContents, passphrase) {
|
|
73
|
+
if (passphrase) {
|
|
74
|
+
const decrypted = await decryptExport(await this.libraryLoader.getOpenPGPLib(), fileContents, passphrase);
|
|
75
|
+
return this.importFromTextFile(decrypted);
|
|
76
|
+
}
|
|
77
|
+
const lines = fileContents.trim().split('\n');
|
|
78
|
+
const result = await processImportLines(lines, (uri) => this.importFromUri(uri));
|
|
79
|
+
// force save to make sure we're not caught in a race condition
|
|
80
|
+
await this.persistentStorageManager.save();
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Import an entry from an OTP URI.
|
|
85
|
+
* @param otpUri - The OTP URI to import
|
|
86
|
+
* @returns A promise that resolves to the newly added EntryId.
|
|
87
|
+
* @throws {ExportImportError} If the URI is invalid or contains unsupported OTP type.
|
|
88
|
+
*/
|
|
89
|
+
async importFromUri(otpUri) {
|
|
90
|
+
const UrlParser = await this.libraryLoader.getUrlParserLib();
|
|
91
|
+
const newEntry = parseOtpUri(UrlParser, otpUri.trim());
|
|
92
|
+
return this.vaultOperationsManager.addEntry(newEntry);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Import an entry from a QR code image.
|
|
96
|
+
* @param imageInput - The image containing the QR code
|
|
97
|
+
* @returns A promise that resolves to the newly added EntryId.
|
|
98
|
+
* @throws {InvalidInputExportImportError} If the QR code is invalid or doesn't contain a valid OTP URI.
|
|
99
|
+
*/
|
|
100
|
+
async importFromQRCode(imageInput) {
|
|
101
|
+
const otpUri = await getDataFromQRImage(this.libraryLoader, imageInput);
|
|
102
|
+
return this.importFromUri(otpUri);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export default ExportImportManager;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type CryptoLib from '../interfaces/CryptoLib.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Class responsible for loading various external (big) libraries required by the application.
|
|
4
|
+
* All libraries (except CryptoLib) are loaded on demand. This helps to reduce the initial bundle size.
|
|
5
|
+
*/
|
|
6
|
+
declare class LibraryLoader {
|
|
7
|
+
private cryptoLib;
|
|
8
|
+
private openPgpLib?;
|
|
9
|
+
private qrGeneratorLib?;
|
|
10
|
+
private jsQrLib?;
|
|
11
|
+
private canvasLib?;
|
|
12
|
+
private urlParserLib?;
|
|
13
|
+
private zxcvbn?;
|
|
14
|
+
/**
|
|
15
|
+
* Constructs a new instance of LibraryLoader.
|
|
16
|
+
* @param cryptoLib - An instance of CryptoLib that is required for library operations.
|
|
17
|
+
* @throws {InitializationError} If the provided CryptoLib instance is invalid.
|
|
18
|
+
*/
|
|
19
|
+
constructor(cryptoLib: CryptoLib);
|
|
20
|
+
/**
|
|
21
|
+
* @returns The CryptoLib instance.
|
|
22
|
+
*/
|
|
23
|
+
getCryptoLib(): CryptoLib;
|
|
24
|
+
/**
|
|
25
|
+
* Loads and returns the OpenPGP library on demand.
|
|
26
|
+
* @returns A promise that resolves to the OpenPGP library.
|
|
27
|
+
*/
|
|
28
|
+
getOpenPGPLib(): Promise<typeof import("openpgp")>;
|
|
29
|
+
/**
|
|
30
|
+
* Loads and returns the QR Generator library on demand.
|
|
31
|
+
* @returns A promise that resolves to the QR Generator library.
|
|
32
|
+
*/
|
|
33
|
+
getQrGeneratorLib(): Promise<typeof import("qrcode")>;
|
|
34
|
+
/**
|
|
35
|
+
* Loads and returns the JsQR library on demand.
|
|
36
|
+
* @returns A promise that resolves to the JsQR library.
|
|
37
|
+
*/
|
|
38
|
+
getJsQrLib(): Promise<typeof import("jsqr").default>;
|
|
39
|
+
/**
|
|
40
|
+
* Loads and returns the Canvas library on demand.
|
|
41
|
+
* @returns A promise that resolves to the Canvas library.
|
|
42
|
+
* @throws {TwoFALibError} If the library cannot be loaded in a browser environment.
|
|
43
|
+
*/
|
|
44
|
+
getCanvasLib(): Promise<typeof import("canvas")>;
|
|
45
|
+
/**
|
|
46
|
+
* Loads and returns the URL Parser library on demand.
|
|
47
|
+
* @returns A promise that resolves to the URL Parser library.
|
|
48
|
+
*/
|
|
49
|
+
getUrlParserLib(): Promise<typeof import("whatwg-url")>;
|
|
50
|
+
/**
|
|
51
|
+
* Loads and returns the Zxcvbn library on demand.
|
|
52
|
+
* @returns A promise that resolves to the Zxcvbn library.
|
|
53
|
+
*/
|
|
54
|
+
getZxcvbn(): Promise<(password: string, userInputs?: (string | number)[]) => import("@zxcvbn-ts/core").ZxcvbnResult>;
|
|
55
|
+
}
|
|
56
|
+
export default LibraryLoader;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { InitializationError, TwoFALibError } from '../TwoFALibError.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Class responsible for loading various external (big) libraries required by the application.
|
|
4
|
+
* All libraries (except CryptoLib) are loaded on demand. This helps to reduce the initial bundle size.
|
|
5
|
+
*/
|
|
6
|
+
class LibraryLoader {
|
|
7
|
+
/**
|
|
8
|
+
* Constructs a new instance of LibraryLoader.
|
|
9
|
+
* @param cryptoLib - An instance of CryptoLib that is required for library operations.
|
|
10
|
+
* @throws {InitializationError} If the provided CryptoLib instance is invalid.
|
|
11
|
+
*/
|
|
12
|
+
constructor(cryptoLib) {
|
|
13
|
+
if (!cryptoLib) {
|
|
14
|
+
throw new InitializationError('CryptoLib is required');
|
|
15
|
+
}
|
|
16
|
+
this.cryptoLib = cryptoLib;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @returns The CryptoLib instance.
|
|
20
|
+
*/
|
|
21
|
+
getCryptoLib() {
|
|
22
|
+
return this.cryptoLib;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Loads and returns the OpenPGP library on demand.
|
|
26
|
+
* @returns A promise that resolves to the OpenPGP library.
|
|
27
|
+
*/
|
|
28
|
+
async getOpenPGPLib() {
|
|
29
|
+
if (!this.openPgpLib) {
|
|
30
|
+
const module = await import('openpgp');
|
|
31
|
+
// enable Authenticated Encryption with Associated Data
|
|
32
|
+
module.config.aeadProtect = true;
|
|
33
|
+
this.openPgpLib = module;
|
|
34
|
+
}
|
|
35
|
+
return this.openPgpLib;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Loads and returns the QR Generator library on demand.
|
|
39
|
+
* @returns A promise that resolves to the QR Generator library.
|
|
40
|
+
*/
|
|
41
|
+
async getQrGeneratorLib() {
|
|
42
|
+
if (!this.qrGeneratorLib) {
|
|
43
|
+
const module = await import('qrcode');
|
|
44
|
+
this.qrGeneratorLib = module.default;
|
|
45
|
+
}
|
|
46
|
+
return this.qrGeneratorLib;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Loads and returns the JsQR library on demand.
|
|
50
|
+
* @returns A promise that resolves to the JsQR library.
|
|
51
|
+
*/
|
|
52
|
+
async getJsQrLib() {
|
|
53
|
+
if (!this.jsQrLib) {
|
|
54
|
+
const module = await import('jsqr');
|
|
55
|
+
this.jsQrLib = module.default.default;
|
|
56
|
+
}
|
|
57
|
+
return this.jsQrLib;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Loads and returns the Canvas library on demand.
|
|
61
|
+
* @returns A promise that resolves to the Canvas library.
|
|
62
|
+
* @throws {TwoFALibError} If the library cannot be loaded in a browser environment.
|
|
63
|
+
*/
|
|
64
|
+
async getCanvasLib() {
|
|
65
|
+
if (typeof window !== 'undefined') {
|
|
66
|
+
throw new TwoFALibError('Canvas lib can not be loaded in browser env');
|
|
67
|
+
}
|
|
68
|
+
if (!this.canvasLib) {
|
|
69
|
+
const module = await import('canvas');
|
|
70
|
+
this.canvasLib = module.default;
|
|
71
|
+
}
|
|
72
|
+
return this.canvasLib;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Loads and returns the URL Parser library on demand.
|
|
76
|
+
* @returns A promise that resolves to the URL Parser library.
|
|
77
|
+
*/
|
|
78
|
+
async getUrlParserLib() {
|
|
79
|
+
if (!this.urlParserLib) {
|
|
80
|
+
const module = await import('whatwg-url');
|
|
81
|
+
this.urlParserLib = module.default;
|
|
82
|
+
}
|
|
83
|
+
return this.urlParserLib;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Loads and returns the Zxcvbn library on demand.
|
|
87
|
+
* @returns A promise that resolves to the Zxcvbn library.
|
|
88
|
+
*/
|
|
89
|
+
async getZxcvbn() {
|
|
90
|
+
if (!this.zxcvbn) {
|
|
91
|
+
const { zxcvbn, zxcvbnOptions } = await import('@zxcvbn-ts/core');
|
|
92
|
+
const zxcvbnCommonPackage = await import('@zxcvbn-ts/language-common');
|
|
93
|
+
const zxcvbnEnPackage = await import('@zxcvbn-ts/language-en');
|
|
94
|
+
const options = {
|
|
95
|
+
translations: zxcvbnEnPackage.translations,
|
|
96
|
+
graphs: zxcvbnCommonPackage.adjacencyGraphs,
|
|
97
|
+
dictionary: {
|
|
98
|
+
...zxcvbnCommonPackage.dictionary,
|
|
99
|
+
...zxcvbnEnPackage.dictionary,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
zxcvbnOptions.setOptions(options);
|
|
103
|
+
this.zxcvbn = zxcvbn;
|
|
104
|
+
}
|
|
105
|
+
return this.zxcvbn;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export default LibraryLoader;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { EncryptedPrivateKey, EncryptedSymmetricKey, Passphrase, PrivateKey, Salt, SymmetricKey } from '../interfaces/CryptoLib.mjs';
|
|
2
|
+
import { EncryptedVaultStateString, LockedRepresentationString } from '../interfaces/Vault.mjs';
|
|
3
|
+
import type TwoFaLibMediator from '../TwoFaLibMediator.mjs';
|
|
4
|
+
import type { DeviceId } from '../interfaces/SyncTypes.mjs';
|
|
5
|
+
import type { PassphraseExtraDict } from '../interfaces/PassphraseExtraDict.js';
|
|
6
|
+
/**
|
|
7
|
+
* Manages all storage of data that should be persistent.
|
|
8
|
+
*/
|
|
9
|
+
declare class PersistentStorageManager {
|
|
10
|
+
private mediator;
|
|
11
|
+
private readonly passphraseExtraDict;
|
|
12
|
+
private readonly deviceId;
|
|
13
|
+
private readonly privateKey;
|
|
14
|
+
private readonly symmetricKey;
|
|
15
|
+
private encryptedPrivateKey;
|
|
16
|
+
private encryptedSymmetricKey;
|
|
17
|
+
private salt;
|
|
18
|
+
static readonly storageVersion = 1;
|
|
19
|
+
/**
|
|
20
|
+
* Constructs a new instance of PersistentStorageManager.
|
|
21
|
+
* @param mediator - The mediator for accessing other components.
|
|
22
|
+
* @param passphraseExtraDict - Additional words to be used for passphrase strength evaluation.
|
|
23
|
+
* @param deviceId - The unique identifier of the device.
|
|
24
|
+
* @param privateKey - The private key used for cryptographic operations.
|
|
25
|
+
* @param symmetricKey - The symmetric key used for cryptographic operations.
|
|
26
|
+
* @param encryptedPrivateKey - The encrypted private key
|
|
27
|
+
* @param encryptedSymmetricKey - The encrypted symmetric key
|
|
28
|
+
* @param salt - The salt used for key derivation.
|
|
29
|
+
*/
|
|
30
|
+
constructor(mediator: TwoFaLibMediator, passphraseExtraDict: PassphraseExtraDict, deviceId: DeviceId, privateKey: PrivateKey, symmetricKey: SymmetricKey, encryptedPrivateKey: EncryptedPrivateKey, encryptedSymmetricKey: EncryptedSymmetricKey, salt: Salt);
|
|
31
|
+
private get cryptoLib();
|
|
32
|
+
private get vaultDataManager();
|
|
33
|
+
private get syncManager();
|
|
34
|
+
private get dispatchLibEvent();
|
|
35
|
+
/**
|
|
36
|
+
* Retrieves an encrypted representation of the library's current state.
|
|
37
|
+
* This can be used for secure storage or transmission of the library's data.
|
|
38
|
+
* @param key - The key to decrypt the locked representation with. If not provided the library's current symmetric key will be used.
|
|
39
|
+
* @returns A promise that resolves with a string representation of the locked state.
|
|
40
|
+
*/
|
|
41
|
+
getEncryptedVaultState(key?: SymmetricKey): Promise<EncryptedVaultStateString>;
|
|
42
|
+
/**
|
|
43
|
+
* Creates a partially encrypted representation of all data, except for
|
|
44
|
+
* the passphrase, that is needed to load the library. This can be used
|
|
45
|
+
* for secure storage of the library's data.
|
|
46
|
+
* @returns A promise that resolves with a json encoded string of
|
|
47
|
+
* the partially encrypted library's data.
|
|
48
|
+
*/
|
|
49
|
+
getLockedRepresentation(): Promise<LockedRepresentationString>;
|
|
50
|
+
/**
|
|
51
|
+
* Saves the current state of the library.
|
|
52
|
+
* @returns A promise that resolves when the save operation is complete.
|
|
53
|
+
*/
|
|
54
|
+
save(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Validates the provided passphrase against the current library passphrase.
|
|
57
|
+
* @param salt - The salt used for key derivation.
|
|
58
|
+
* @param passphrase - The passphrase to validate.
|
|
59
|
+
* @returns A promise that resolves with a boolean indicating whether the passphrase is valid.
|
|
60
|
+
*/
|
|
61
|
+
validatePassphrase(salt: Salt, passphrase: Passphrase): Promise<boolean>;
|
|
62
|
+
/**
|
|
63
|
+
* Changes the library's passphrase.
|
|
64
|
+
* @param oldPassphrase - The current passphrase.
|
|
65
|
+
* @param newPassphrase - The new passphrase to set.
|
|
66
|
+
* @returns A promise that resolves when the passphrase change is complete.
|
|
67
|
+
* @throws {AuthenticationError} If the provided old passphrase is incorrect.
|
|
68
|
+
*/
|
|
69
|
+
changePassphrase(oldPassphrase: Passphrase, newPassphrase: Passphrase): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
export default PersistentStorageManager;
|