1
1
import { SecureDataView } from "../utils/secureDataView" ;
2
2
import type { IdbConfig } from "../crypto/DeviceKeyProvider" ;
3
+ /**
4
+ * Configuration options for initializing SecureLocalStorage.
5
+ */
3
6
export interface SecureLocalStorageOptions {
4
- /** Override the localStorage key (for multi-tenant apps or tests). */
7
+ /**
8
+ * Overrides the default key used for storing encrypted data in `localStorage`.
9
+ * This is useful for multi-tenant applications or for isolating data in tests.
10
+ * @default "secure-local-storage"
11
+ */
5
12
storageKey ?: string ;
6
- /** Override IndexedDB configuration (for multi-tenant apps or tests). */
13
+ /**
14
+ * Overrides the default IndexedDB configuration for storing the device-specific key.
15
+ * This is useful for multi-tenant applications or for isolating data in tests.
16
+ */
7
17
idbConfig ?: Partial < IdbConfig > ;
8
18
}
19
+ /**
20
+ * Provides a secure, client-side storage solution that encrypts data before persisting it.
21
+ *
22
+ * `SecureLocalStorage` offers two primary modes of operation:
23
+ * 1. **Device-bound Mode**: (Default) Data is encrypted with a key that is stored in
24
+ * the browser's IndexedDB. This key is unique to the device and profile, making it
25
+ * difficult to access from other devices. Data is automatically unlocked when the
26
+ * class is instantiated.
27
+ * 2. **Master Password Mode**: Data is encrypted with a key derived from a user-provided
28
+ * master password. The data can only be accessed by providing the correct password,
29
+ * allowing for portability across devices but requiring user interaction to unlock.
30
+ *
31
+ * The class handles key management, encryption, and data serialization, providing a
32
+ * simple `getData`/`setData` interface for application use. It also supports features
33
+ * like key rotation, data export/import, and changing or removing the master password.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // Initialize in device-bound mode
38
+ * const sls = new SecureLocalStorage();
39
+ *
40
+ * // Set some data
41
+ * await sls.setData({ mySecret: "hello world" });
42
+ *
43
+ * // Get the data back
44
+ * const dataView = await sls.getData();
45
+ * console.log(dataView.value.mySecret); // "hello world"
46
+ *
47
+ * // Wipe the plaintext from memory
48
+ * dataView.wipe();
49
+ * ```
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * // Initialize and set a master password
54
+ * const sls = new SecureLocalStorage();
55
+ * await sls.setMasterPassword("my-strong-password-123");
56
+ *
57
+ * // Later, in a new session
58
+ * const sls2 = new SecureLocalStorage();
59
+ * await sls2.unlock("my-strong-password-123");
60
+ * const data = await sls2.getData();
61
+ * // ... use data
62
+ * sls2.lock(); // clear session keys
63
+ * ```
64
+ */
9
65
export declare class SecureLocalStorage {
10
66
private readonly store ;
11
67
private readonly enc ;
@@ -14,39 +70,134 @@ export declare class SecureLocalStorage {
14
70
private dek ;
15
71
private ready ;
16
72
private readonly idbConfig ;
73
+ /**
74
+ * The current version of the data structure format.
75
+ */
17
76
readonly DATA_VERSION : number ;
77
+ /**
78
+ * Initializes a new instance of SecureLocalStorage.
79
+ *
80
+ * The constructor immediately begins an asynchronous initialization process.
81
+ * Public methods will await this process, so you don't need to manually wait
82
+ * for it to complete.
83
+ *
84
+ * @param opts - Optional configuration to customize storage keys.
85
+ */
18
86
constructor ( opts ?: SecureLocalStorageOptions ) ;
87
+ /**
88
+ * Checks if the storage is currently protected by a master password.
89
+ * @returns `true` if a master password is set, `false` otherwise.
90
+ */
19
91
isUsingMasterPassword ( ) : boolean ;
20
- /** Unlock session with master password (no-op in device mode / no data available ). */
92
+ /**
93
+ * Unlocks the data encryption key (DEK) using the provided master password.
94
+ * This is required to access data when in master password mode.
95
+ * If the store is in device-bound mode or is already unlocked, this method does nothing.
96
+ *
97
+ * @param masterPassword - The user's master password.
98
+ * @throws {ValidationError } If the master password is an empty string or invalid.
99
+ */
21
100
unlock ( masterPassword : string ) : Promise < void > ;
22
- /** Set a master password (switch from device mode to master mode). */
101
+ /**
102
+ * Sets a master password, switching from device-bound mode to master password mode.
103
+ * This re-encrypts the data encryption key (DEK) with a new key derived from the password.
104
+ *
105
+ * @param masterPassword - The new master password to set. Must be a non-empty string.
106
+ * @throws {ModeError } If a master password is already set.
107
+ * @throws {ValidationError } If the master password is an empty string.
108
+ */
23
109
setMasterPassword ( masterPassword : string ) : Promise < void > ;
24
- /** Remove master password, re-wrapping DEK with device-bound KEK. Requires unlocked session. */
110
+ /**
111
+ * Removes the master password, switching back to device-bound mode.
112
+ * The DEK is re-encrypted using the device-specific key.
113
+ * Requires the session to be unlocked.
114
+ *
115
+ * @throws {ModeError } If no master password is set.
116
+ * @throws {LockedError } If the session is locked.
117
+ */
25
118
removeMasterPassword ( ) : Promise < void > ;
26
- /** Rotate master password atomically. */
119
+ /**
120
+ * Atomically changes the master password.
121
+ * If not in master password mode, it will set the new password.
122
+ *
123
+ * @param oldMasterPassword - The current master password.
124
+ * @param newMasterPassword - The new master password. Must be a non-empty string.
125
+ * @throws {ValidationError } If the new password is empty or if the old password is incorrect.
126
+ * @throws {LockedError } If the session is locked.
127
+ */
27
128
rotateMasterPassword ( oldMasterPassword : string , newMasterPassword : string ) : Promise < void > ;
28
- /** Lock the session (clears derived KEK & DEK from memory). */
129
+ /**
130
+ * Locks the session by clearing the cached Key Encryption Key (KEK) and
131
+ * Data Encryption Key (DEK) from memory.
132
+ * After locking, `unlock()` must be called to perform further operations.
133
+ */
29
134
lock ( ) : void ;
30
- /** Rotate DEK and device KEK. Allowed only in password-less mode. */
135
+ /**
136
+ * Rotates the Data Encryption Key (DEK) and the device-specific Key Encryption Key (KEK).
137
+ * This enhances security by replacing the keys used to protect the data.
138
+ * This operation is only allowed in device-bound mode.
139
+ *
140
+ * @throws {ModeError } If a master password is set.
141
+ */
31
142
rotateKeys ( ) : Promise < void > ;
32
- /** Get decrypted data as a wipeable view object. */
143
+ /**
144
+ * Retrieves the decrypted data.
145
+ *
146
+ * @returns A promise that resolves to a `SecureDataView`, a wrapper around the
147
+ * decrypted data object that includes a `wipe()` method to securely
148
+ * clear the plaintext data from memory.
149
+ * @throws {LockedError } If the session is locked.
150
+ * @throws {ValidationError } If the stored data is not a plain object.
151
+ * @template T The expected type of the stored data object.
152
+ */
33
153
getData < T extends Record < string , unknown > = Record < string , unknown > > ( ) : Promise < SecureDataView < T > > ;
34
- /** Replace data with the provided plain object. */
154
+ /**
155
+ * Encrypts and persists the provided data object, replacing any existing data.
156
+ *
157
+ * @param value The plain JavaScript object to store. It must be serializable.
158
+ * @throws {LockedError } If the session is locked.
159
+ * @throws {ValidationError } If the provided value is not a plain object.
160
+ */
35
161
setData < T extends Record < string , unknown > > ( value : T ) : Promise < void > ;
36
162
/**
37
- * Export the encrypted bundle as JSON string.
38
- * - If `customExportPassword` provided: derive export KEK (Argon2id) and rewrap DEK accordingly (mPw=false).
39
- * - If absent and in master mode: exports current config wrapped with master password (mPw=true).
163
+ * Exports the encrypted data bundle as a JSON string.
164
+ *
165
+ * There are two export modes:
166
+ * 1. **Master Password Mode**: If no `customExportPassword` is provided and a master
167
+ * password is set, the bundle is exported using the existing master password.
168
+ * 2. **Custom Password Mode**: If a `customExportPassword` is provided, the bundle
169
+ * is re-encrypted with a key derived from this password. This is required when
170
+ * in device-bound mode.
171
+ *
172
+ * @param customExportPassword - An optional password to protect the exported data.
173
+ * Required if not in master password mode.
174
+ * @returns A JSON string representing the encrypted data bundle.
175
+ * @throws {ExportError } If a password is required but not provided, or if the
176
+ * provided password is invalid.
40
177
*/
41
178
exportData ( customExportPassword ?: string ) : Promise < string > ;
42
179
/**
43
- * Import previously exported JSON.
44
- * - If bundle.mPw===true OR header.rounds>1 and no mPw flag: expects master password.
45
- * - Else expects export password.
46
- * After import, rewrap to device mode if using export password.
180
+ * Imports a previously exported data bundle.
181
+ *
182
+ * The method determines whether to use a master password or an export password
183
+ * based on the bundle's metadata. After a successful import using an export
184
+ * password, the data is re-encrypted into device-bound mode. If imported with a
185
+ * master password, it remains in master password mode.
186
+ *
187
+ * @param serialized - The JSON string of the exported data bundle.
188
+ * @param password - The password required to decrypt the bundle (master or export).
189
+ * @returns A promise that resolves to 'masterPassword' or 'customExportPassword'
190
+ * indicating which type of password was used for the import.
191
+ * @throws {ImportError } If the JSON is invalid, the bundle is corrupted, the
192
+ * password is required but missing, or the password is incorrect.
47
193
*/
48
194
importData ( serialized : string , password ?: string ) : Promise < string > ;
49
- /** Clear all data (localStorage + IndexedDB KEK) and reinitialize fresh empty store in device mode. */
195
+ /**
196
+ * Clears all stored data, including the encrypted bundle from `localStorage` and
197
+ * the device key from `IndexedDB`.
198
+ * After clearing, the instance is reinitialized to a fresh, empty state in
199
+ * device-bound mode.
200
+ */
50
201
clear ( ) : Promise < void > ;
51
202
private initialize ;
52
203
private persist ;
0 commit comments