@alibarbar/common 1.0.10 → 1.1.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/dist/algorithm.cjs +1 -1
- package/dist/algorithm.js +1 -1
- package/dist/array.cjs +1 -1
- package/dist/array.js +1 -1
- package/dist/color.cjs +1 -1
- package/dist/color.js +1 -1
- package/dist/crypto.cjs +109 -1
- package/dist/crypto.d.mts +48 -1
- package/dist/crypto.d.ts +48 -1
- package/dist/crypto.js +104 -2
- package/dist/data-structure.cjs +1 -1
- package/dist/data-structure.js +1 -1
- package/dist/date.cjs +1 -1
- package/dist/date.js +1 -1
- package/dist/dom.cjs +1 -1
- package/dist/dom.js +1 -1
- package/dist/file.cjs +1 -1
- package/dist/file.js +1 -1
- package/dist/i18n.cjs +1 -1
- package/dist/i18n.js +1 -1
- package/dist/index.cjs +848 -415
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +838 -413
- package/dist/network.cjs +1 -1
- package/dist/network.js +1 -1
- package/dist/number.cjs +1 -1
- package/dist/number.js +1 -1
- package/dist/object.cjs +1 -1
- package/dist/object.js +1 -1
- package/dist/performance.cjs +1 -1
- package/dist/performance.js +1 -1
- package/dist/storage.cjs +509 -104
- package/dist/storage.d.mts +50 -73
- package/dist/storage.d.ts +50 -73
- package/dist/storage.js +505 -102
- package/dist/string.cjs +1 -1
- package/dist/string.js +1 -1
- package/dist/tracking.cjs +1 -1
- package/dist/tracking.js +1 -1
- package/dist/transform.cjs +1 -1
- package/dist/transform.js +1 -1
- package/dist/upload.cjs +2 -2
- package/dist/upload.d.mts +1 -1
- package/dist/upload.d.ts +1 -1
- package/dist/upload.js +2 -2
- package/dist/url.cjs +1 -1
- package/dist/url.js +1 -1
- package/dist/validation.cjs +1 -1
- package/dist/validation.js +1 -1
- package/package.json +3 -1
- /package/dist/{upload-DchqyDBQ.d.mts → index-DchqyDBQ.d.mts} +0 -0
- /package/dist/{upload-DchqyDBQ.d.ts → index-DchqyDBQ.d.ts} +0 -0
package/dist/storage.js
CHANGED
|
@@ -1,36 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __esm = (fn, res) => function __init() {
|
|
4
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
-
};
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
// src/helper/crypto.ts
|
|
12
|
-
var crypto_exports = {};
|
|
13
|
-
__export(crypto_exports, {
|
|
14
|
-
base64Decode: () => base64Decode,
|
|
15
|
-
base64Encode: () => base64Encode,
|
|
16
|
-
exportPrivateKey: () => exportPrivateKey,
|
|
17
|
-
exportPublicKey: () => exportPublicKey,
|
|
18
|
-
generateRSAKeyPair: () => generateRSAKeyPair,
|
|
19
|
-
generateRandomString: () => generateRandomString,
|
|
20
|
-
generateUUID: () => generateUUID,
|
|
21
|
-
hash: () => hash,
|
|
22
|
-
importPrivateKey: () => importPrivateKey,
|
|
23
|
-
importPublicKey: () => importPublicKey,
|
|
24
|
-
rsaDecrypt: () => rsaDecrypt,
|
|
25
|
-
rsaEncrypt: () => rsaEncrypt,
|
|
26
|
-
sha256: () => sha256
|
|
27
|
-
});
|
|
28
|
-
async function sha256(data) {
|
|
29
|
-
const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
30
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
|
|
31
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
32
|
-
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
33
|
-
}
|
|
1
|
+
// src/helper/crypto/index.ts
|
|
34
2
|
function base64Encode(data) {
|
|
35
3
|
if (typeof data === "string") {
|
|
36
4
|
return btoa(unescape(encodeURIComponent(data)));
|
|
@@ -49,16 +17,6 @@ function base64Decode(data) {
|
|
|
49
17
|
throw new Error("Invalid Base64 string");
|
|
50
18
|
}
|
|
51
19
|
}
|
|
52
|
-
function generateUUID() {
|
|
53
|
-
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
54
|
-
return crypto.randomUUID();
|
|
55
|
-
}
|
|
56
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
57
|
-
const r = Math.random() * 16 | 0;
|
|
58
|
-
const v = c === "x" ? r : r & 3 | 8;
|
|
59
|
-
return v.toString(16);
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
20
|
function generateRandomString(length, charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
|
|
63
21
|
let result = "";
|
|
64
22
|
for (let i = 0; i < length; i++) {
|
|
@@ -66,15 +24,6 @@ function generateRandomString(length, charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcde
|
|
|
66
24
|
}
|
|
67
25
|
return result;
|
|
68
26
|
}
|
|
69
|
-
function hash(data) {
|
|
70
|
-
let hashValue = 0;
|
|
71
|
-
for (let i = 0; i < data.length; i++) {
|
|
72
|
-
const char = data.charCodeAt(i);
|
|
73
|
-
hashValue = (hashValue << 5) - hashValue + char;
|
|
74
|
-
hashValue = hashValue & hashValue;
|
|
75
|
-
}
|
|
76
|
-
return Math.abs(hashValue);
|
|
77
|
-
}
|
|
78
27
|
async function generateRSAKeyPair(modulusLength = 2048) {
|
|
79
28
|
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
80
29
|
throw new Error("Web Crypto API is not available");
|
|
@@ -195,33 +144,428 @@ async function importPrivateKey(keyData) {
|
|
|
195
144
|
["decrypt"]
|
|
196
145
|
);
|
|
197
146
|
}
|
|
198
|
-
|
|
199
|
-
"
|
|
147
|
+
async function generateHMACKey() {
|
|
148
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
149
|
+
throw new Error("Web Crypto API is not available");
|
|
200
150
|
}
|
|
201
|
-
|
|
151
|
+
return crypto.subtle.generateKey(
|
|
152
|
+
{
|
|
153
|
+
name: "HMAC",
|
|
154
|
+
hash: "SHA-256"
|
|
155
|
+
},
|
|
156
|
+
true,
|
|
157
|
+
["sign", "verify"]
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
async function computeHMAC(data, key) {
|
|
161
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
162
|
+
throw new Error("Web Crypto API is not available");
|
|
163
|
+
}
|
|
164
|
+
const buffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
165
|
+
const signature = await crypto.subtle.sign("HMAC", key, buffer);
|
|
166
|
+
return base64Encode(signature);
|
|
167
|
+
}
|
|
168
|
+
async function verifyHMAC(data, signature, key) {
|
|
169
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
170
|
+
throw new Error("Web Crypto API is not available");
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
174
|
+
const signatureBuffer = new Uint8Array(
|
|
175
|
+
atob(signature).split("").map((char) => char.charCodeAt(0))
|
|
176
|
+
);
|
|
177
|
+
return await crypto.subtle.verify("HMAC", key, signatureBuffer, dataBuffer);
|
|
178
|
+
} catch {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function deriveKeyFromPassword(password, salt, iterations = 1e5, keyLength = 256) {
|
|
183
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
184
|
+
throw new Error("Web Crypto API is not available");
|
|
185
|
+
}
|
|
186
|
+
const saltBuffer = typeof salt === "string" ? new TextEncoder().encode(salt) : salt;
|
|
187
|
+
const passwordKey = await crypto.subtle.importKey(
|
|
188
|
+
"raw",
|
|
189
|
+
new TextEncoder().encode(password),
|
|
190
|
+
"PBKDF2",
|
|
191
|
+
false,
|
|
192
|
+
["deriveBits", "deriveKey"]
|
|
193
|
+
);
|
|
194
|
+
return crypto.subtle.deriveKey(
|
|
195
|
+
{
|
|
196
|
+
name: "PBKDF2",
|
|
197
|
+
salt: saltBuffer,
|
|
198
|
+
iterations,
|
|
199
|
+
hash: "SHA-256"
|
|
200
|
+
},
|
|
201
|
+
passwordKey,
|
|
202
|
+
{
|
|
203
|
+
name: "AES-GCM",
|
|
204
|
+
length: keyLength
|
|
205
|
+
},
|
|
206
|
+
false,
|
|
207
|
+
["encrypt", "decrypt"]
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
async function aesGCMEncrypt(data, key) {
|
|
211
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
212
|
+
throw new Error("Web Crypto API is not available");
|
|
213
|
+
}
|
|
214
|
+
const dataBuffer = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
215
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
216
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
217
|
+
{
|
|
218
|
+
name: "AES-GCM",
|
|
219
|
+
iv
|
|
220
|
+
},
|
|
221
|
+
key,
|
|
222
|
+
dataBuffer
|
|
223
|
+
);
|
|
224
|
+
return {
|
|
225
|
+
encrypted: base64Encode(encrypted),
|
|
226
|
+
iv: base64Encode(iv.buffer)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
async function aesGCMDecrypt(encryptedData, iv, key) {
|
|
230
|
+
if (typeof crypto === "undefined" || !crypto.subtle) {
|
|
231
|
+
throw new Error("Web Crypto API is not available");
|
|
232
|
+
}
|
|
233
|
+
const encryptedBuffer = new Uint8Array(
|
|
234
|
+
atob(encryptedData).split("").map((char) => char.charCodeAt(0))
|
|
235
|
+
);
|
|
236
|
+
const ivBuffer = new Uint8Array(
|
|
237
|
+
atob(iv).split("").map((char) => char.charCodeAt(0))
|
|
238
|
+
);
|
|
239
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
240
|
+
{
|
|
241
|
+
name: "AES-GCM",
|
|
242
|
+
iv: ivBuffer
|
|
243
|
+
},
|
|
244
|
+
key,
|
|
245
|
+
encryptedBuffer
|
|
246
|
+
);
|
|
247
|
+
return new TextDecoder().decode(decrypted);
|
|
248
|
+
}
|
|
202
249
|
|
|
203
|
-
// src/browser/
|
|
204
|
-
init_crypto();
|
|
250
|
+
// src/browser/SecureStorage/index.ts
|
|
205
251
|
var globalKeyPair = null;
|
|
252
|
+
var globalHMACKey = null;
|
|
253
|
+
var keyPairInitialized = false;
|
|
254
|
+
var initOptions = {
|
|
255
|
+
autoGenerateKeys: true,
|
|
256
|
+
persistKeys: false,
|
|
257
|
+
keyStorageKey: void 0,
|
|
258
|
+
// 将自动生成随机键名
|
|
259
|
+
keyEncryptionPassword: void 0,
|
|
260
|
+
pbkdf2Iterations: 1e5,
|
|
261
|
+
keyModulusLength: 2048,
|
|
262
|
+
enableHMAC: true,
|
|
263
|
+
enableTimestampValidation: true,
|
|
264
|
+
timestampMaxAge: 7 * 24 * 60 * 60 * 1e3,
|
|
265
|
+
// 7 天
|
|
266
|
+
isProduction: false
|
|
267
|
+
};
|
|
268
|
+
var actualKeyStorageKey = null;
|
|
269
|
+
var keyUsageCount = 0;
|
|
270
|
+
var initializationPromise = null;
|
|
271
|
+
function generateKeyStorageKey() {
|
|
272
|
+
return `_sk_${generateRandomString(32)}_${Date.now()}`;
|
|
273
|
+
}
|
|
274
|
+
async function initializeStorageKeys(options = {}) {
|
|
275
|
+
if (options.forceReinitialize) {
|
|
276
|
+
globalKeyPair = null;
|
|
277
|
+
globalHMACKey = null;
|
|
278
|
+
keyPairInitialized = false;
|
|
279
|
+
actualKeyStorageKey = null;
|
|
280
|
+
keyUsageCount = 0;
|
|
281
|
+
initializationPromise = null;
|
|
282
|
+
} else if (keyPairInitialized && globalKeyPair) {
|
|
283
|
+
initOptions = { ...initOptions, ...options };
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
initOptions = { ...initOptions, ...options };
|
|
287
|
+
if (initOptions.keyStorageKey) {
|
|
288
|
+
actualKeyStorageKey = initOptions.keyStorageKey;
|
|
289
|
+
} else {
|
|
290
|
+
actualKeyStorageKey = generateKeyStorageKey();
|
|
291
|
+
}
|
|
292
|
+
if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
|
|
293
|
+
try {
|
|
294
|
+
const storedKeys = window.localStorage.getItem(actualKeyStorageKey);
|
|
295
|
+
if (storedKeys) {
|
|
296
|
+
const keyData = JSON.parse(storedKeys);
|
|
297
|
+
if (keyData.encrypted && keyData.privateKeyEncrypted && keyData.salt) {
|
|
298
|
+
if (!initOptions.keyEncryptionPassword) {
|
|
299
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
300
|
+
console.error("Encrypted keys found but no password provided");
|
|
301
|
+
}
|
|
302
|
+
throw new Error("Password required to decrypt stored keys");
|
|
303
|
+
}
|
|
304
|
+
const saltArray = new Uint8Array(
|
|
305
|
+
atob(keyData.salt).split("").map((char) => char.charCodeAt(0))
|
|
306
|
+
);
|
|
307
|
+
const iterations = keyData.pbkdf2Iterations || initOptions.pbkdf2Iterations;
|
|
308
|
+
const derivedKey = await deriveKeyFromPassword(
|
|
309
|
+
initOptions.keyEncryptionPassword,
|
|
310
|
+
saltArray.buffer,
|
|
311
|
+
iterations
|
|
312
|
+
);
|
|
313
|
+
const decryptedPrivateKey = await aesGCMDecrypt(
|
|
314
|
+
keyData.privateKeyEncrypted,
|
|
315
|
+
keyData.iv,
|
|
316
|
+
derivedKey
|
|
317
|
+
);
|
|
318
|
+
globalKeyPair = {
|
|
319
|
+
publicKey: await importPublicKey(keyData.publicKey),
|
|
320
|
+
privateKey: await importPrivateKey(decryptedPrivateKey)
|
|
321
|
+
};
|
|
322
|
+
} else if (keyData.publicKey && keyData.privateKey) {
|
|
323
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
324
|
+
console.warn(
|
|
325
|
+
"\u26A0\uFE0F SECURITY WARNING: Loading unencrypted keys from storage. Consider re-initializing with password encryption."
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
globalKeyPair = {
|
|
329
|
+
publicKey: await importPublicKey(keyData.publicKey),
|
|
330
|
+
privateKey: await importPrivateKey(keyData.privateKey)
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
if (globalKeyPair) {
|
|
334
|
+
keyPairInitialized = true;
|
|
335
|
+
if (initOptions.enableHMAC && !globalHMACKey) {
|
|
336
|
+
globalHMACKey = await generateHMACKey();
|
|
337
|
+
}
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
} catch (error) {
|
|
342
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
343
|
+
console.warn("Failed to load persisted keys, will generate new ones");
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (initOptions.autoGenerateKeys && !globalKeyPair) {
|
|
348
|
+
try {
|
|
349
|
+
globalKeyPair = await generateRSAKeyPair(initOptions.keyModulusLength);
|
|
350
|
+
if (initOptions.enableHMAC && !globalHMACKey) {
|
|
351
|
+
globalHMACKey = await generateHMACKey();
|
|
352
|
+
}
|
|
353
|
+
keyPairInitialized = true;
|
|
354
|
+
keyUsageCount = 0;
|
|
355
|
+
if (initOptions.persistKeys && typeof window !== "undefined" && window.localStorage) {
|
|
356
|
+
try {
|
|
357
|
+
const publicKeyStr = await exportPublicKey(globalKeyPair.publicKey);
|
|
358
|
+
if (initOptions.keyEncryptionPassword) {
|
|
359
|
+
const privateKeyStr = await exportPrivateKey(globalKeyPair.privateKey);
|
|
360
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
361
|
+
const derivedKey = await deriveKeyFromPassword(
|
|
362
|
+
initOptions.keyEncryptionPassword,
|
|
363
|
+
salt.buffer,
|
|
364
|
+
initOptions.pbkdf2Iterations
|
|
365
|
+
);
|
|
366
|
+
const { encrypted, iv } = await aesGCMEncrypt(privateKeyStr, derivedKey);
|
|
367
|
+
const keyData = {
|
|
368
|
+
encrypted: true,
|
|
369
|
+
publicKey: publicKeyStr,
|
|
370
|
+
privateKeyEncrypted: encrypted,
|
|
371
|
+
iv,
|
|
372
|
+
salt: base64Encode(salt.buffer),
|
|
373
|
+
pbkdf2Iterations: initOptions.pbkdf2Iterations
|
|
374
|
+
};
|
|
375
|
+
window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
|
|
376
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.info) {
|
|
377
|
+
console.info("\u2705 Keys encrypted and stored securely");
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
381
|
+
console.warn(
|
|
382
|
+
"\u26A0\uFE0F SECURITY WARNING: Storing private keys without encryption! Private keys will be stored in plain text (Base64 encoded). Provide keyEncryptionPassword for secure storage."
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
const keyData = {
|
|
386
|
+
publicKey: publicKeyStr,
|
|
387
|
+
privateKey: await exportPrivateKey(globalKeyPair.privateKey)
|
|
388
|
+
};
|
|
389
|
+
window.localStorage.setItem(actualKeyStorageKey, JSON.stringify(keyData));
|
|
390
|
+
}
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
393
|
+
console.error("Failed to persist keys");
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
} catch (error) {
|
|
398
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
399
|
+
console.error("Failed to generate storage keys");
|
|
400
|
+
}
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (initOptions.enableHMAC && !globalHMACKey) {
|
|
405
|
+
globalHMACKey = await generateHMACKey();
|
|
406
|
+
}
|
|
407
|
+
}
|
|
206
408
|
function setStorageKeyPair(keyPair) {
|
|
207
409
|
globalKeyPair = keyPair;
|
|
410
|
+
keyPairInitialized = true;
|
|
208
411
|
}
|
|
209
412
|
function getStorageKeyPair() {
|
|
210
413
|
return globalKeyPair;
|
|
211
414
|
}
|
|
212
|
-
|
|
213
|
-
|
|
415
|
+
function clearPersistedKeys() {
|
|
416
|
+
if (typeof window !== "undefined" && window.localStorage && actualKeyStorageKey) {
|
|
417
|
+
try {
|
|
418
|
+
window.localStorage.removeItem(actualKeyStorageKey);
|
|
419
|
+
actualKeyStorageKey = null;
|
|
420
|
+
keyPairInitialized = false;
|
|
421
|
+
globalKeyPair = null;
|
|
422
|
+
globalHMACKey = null;
|
|
423
|
+
keyUsageCount = 0;
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
426
|
+
console.error("Failed to clear persisted keys");
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function getKeyUsageCount() {
|
|
432
|
+
return keyUsageCount;
|
|
433
|
+
}
|
|
434
|
+
function resetKeyUsageCount() {
|
|
435
|
+
keyUsageCount = 0;
|
|
214
436
|
}
|
|
215
|
-
async function
|
|
437
|
+
async function ensureKeyPair() {
|
|
438
|
+
if (globalKeyPair) {
|
|
439
|
+
keyUsageCount++;
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (!initOptions.autoGenerateKeys) {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (initializationPromise) {
|
|
446
|
+
await initializationPromise;
|
|
447
|
+
if (globalKeyPair) {
|
|
448
|
+
keyUsageCount++;
|
|
449
|
+
}
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
initializationPromise = initializeStorageKeys();
|
|
216
453
|
try {
|
|
217
|
-
|
|
454
|
+
await initializationPromise;
|
|
455
|
+
} finally {
|
|
456
|
+
initializationPromise = null;
|
|
457
|
+
}
|
|
458
|
+
if (globalKeyPair) {
|
|
459
|
+
keyUsageCount++;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function safeParseJSON(jsonStr, expectedType) {
|
|
463
|
+
try {
|
|
464
|
+
const parsed = JSON.parse(jsonStr);
|
|
465
|
+
if (expectedType === "object" && (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))) {
|
|
466
|
+
throw new Error("Expected object but got different type");
|
|
467
|
+
}
|
|
468
|
+
if (expectedType === "array" && !Array.isArray(parsed)) {
|
|
469
|
+
throw new Error("Expected array but got different type");
|
|
470
|
+
}
|
|
471
|
+
return parsed;
|
|
472
|
+
} catch (error) {
|
|
473
|
+
if (error instanceof SyntaxError) {
|
|
474
|
+
throw new Error(`Invalid JSON format: ${error.message}`);
|
|
475
|
+
}
|
|
476
|
+
throw error;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
function validateTimestamp(timestamp, maxClockSkew = 5 * 60 * 1e3) {
|
|
480
|
+
if (!initOptions.enableTimestampValidation || !timestamp) {
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
if (initOptions.timestampMaxAge === 0) {
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
const now = Date.now();
|
|
487
|
+
const age = now - timestamp;
|
|
488
|
+
const maxAge = initOptions.timestampMaxAge || 7 * 24 * 60 * 60 * 1e3;
|
|
489
|
+
if (age < -maxClockSkew) {
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
return age >= -maxClockSkew && age <= maxAge;
|
|
493
|
+
}
|
|
494
|
+
async function encryptValue(value, publicKey, hmacKey) {
|
|
495
|
+
const encryptedData = await rsaEncrypt(value, publicKey);
|
|
496
|
+
if (hmacKey) {
|
|
497
|
+
const hmac = await computeHMAC(encryptedData, hmacKey);
|
|
498
|
+
const item = {
|
|
499
|
+
data: encryptedData,
|
|
500
|
+
hmac,
|
|
501
|
+
encrypted: true,
|
|
502
|
+
timestamp: Date.now()
|
|
503
|
+
};
|
|
504
|
+
return JSON.stringify(item);
|
|
505
|
+
}
|
|
506
|
+
return encryptedData;
|
|
507
|
+
}
|
|
508
|
+
async function handleDecryptError(error, encryptedStr, key) {
|
|
509
|
+
if (error instanceof Error && error.message.includes("HMAC verification failed")) {
|
|
510
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
511
|
+
console.error(
|
|
512
|
+
"\u26A0\uFE0F SECURITY ALERT: Data integrity check failed! Data may have been tampered with."
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
throw error;
|
|
516
|
+
}
|
|
517
|
+
if (error instanceof Error && error.message.includes("Data timestamp validation failed")) {
|
|
518
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
519
|
+
console.warn("\u26A0\uFE0F SECURITY WARNING: Data timestamp validation failed");
|
|
520
|
+
}
|
|
521
|
+
throw error;
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
const decryptedStr = base64Decode(encryptedStr);
|
|
525
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
526
|
+
console.warn(
|
|
527
|
+
`\u26A0\uFE0F SECURITY WARNING: Reading unencrypted data for key "${key}". This may be a security risk if sensitive data was stored without encryption.`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
return decryptedStr;
|
|
218
531
|
} catch {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
532
|
+
throw error;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function handleNoKeyDecode(encryptedStr, key) {
|
|
536
|
+
try {
|
|
537
|
+
const decryptedStr = base64Decode(encryptedStr);
|
|
538
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
539
|
+
console.warn(
|
|
540
|
+
`\u26A0\uFE0F SECURITY WARNING: Reading unencrypted data for key "${key}" without encryption keys.`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
return decryptedStr;
|
|
544
|
+
} catch (error) {
|
|
545
|
+
throw new Error(`Failed to decode storage value for key "${key}"`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
async function decryptValue(encryptedValue, privateKey, hmacKey) {
|
|
549
|
+
try {
|
|
550
|
+
const item = JSON.parse(encryptedValue);
|
|
551
|
+
if (item.encrypted && item.data && item.hmac) {
|
|
552
|
+
if (hmacKey) {
|
|
553
|
+
const isValid = await verifyHMAC(item.data, item.hmac, hmacKey);
|
|
554
|
+
if (!isValid) {
|
|
555
|
+
throw new Error("HMAC verification failed: data may have been tampered with");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (item.timestamp && !validateTimestamp(item.timestamp)) {
|
|
559
|
+
throw new Error("Data timestamp validation failed: data may be expired or replayed");
|
|
560
|
+
}
|
|
561
|
+
return await rsaDecrypt(item.data, privateKey);
|
|
562
|
+
}
|
|
563
|
+
return await rsaDecrypt(encryptedValue, privateKey);
|
|
564
|
+
} catch (error) {
|
|
565
|
+
if (error instanceof Error && error.message.includes("HMAC verification failed")) {
|
|
566
|
+
throw error;
|
|
224
567
|
}
|
|
568
|
+
throw new Error("Failed to decrypt storage value: invalid format or corrupted data");
|
|
225
569
|
}
|
|
226
570
|
}
|
|
227
571
|
var localStorage = {
|
|
@@ -243,17 +587,24 @@ var localStorage = {
|
|
|
243
587
|
};
|
|
244
588
|
try {
|
|
245
589
|
const jsonStr = JSON.stringify(item);
|
|
590
|
+
await ensureKeyPair();
|
|
246
591
|
const keyToUse = publicKey || globalKeyPair?.publicKey;
|
|
247
592
|
if (keyToUse) {
|
|
248
|
-
const encrypted = await encryptValue(jsonStr, keyToUse);
|
|
593
|
+
const encrypted = await encryptValue(jsonStr, keyToUse, globalHMACKey);
|
|
249
594
|
window.localStorage.setItem(key, encrypted);
|
|
250
595
|
} else {
|
|
251
|
-
|
|
252
|
-
|
|
596
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
597
|
+
console.warn(
|
|
598
|
+
`\u26A0\uFE0F SECURITY WARNING: Storing data without encryption for key "${key}". Data is only Base64 encoded, not encrypted!`
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
const encoded = base64Encode(jsonStr);
|
|
253
602
|
window.localStorage.setItem(key, encoded);
|
|
254
603
|
}
|
|
255
604
|
} catch (error) {
|
|
256
|
-
|
|
605
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
606
|
+
console.error("localStorage.set error:", error);
|
|
607
|
+
}
|
|
257
608
|
throw error;
|
|
258
609
|
}
|
|
259
610
|
},
|
|
@@ -271,34 +622,36 @@ var localStorage = {
|
|
|
271
622
|
try {
|
|
272
623
|
const encryptedStr = window.localStorage.getItem(key);
|
|
273
624
|
if (!encryptedStr) return defaultValue;
|
|
625
|
+
await ensureKeyPair();
|
|
274
626
|
let decryptedStr;
|
|
275
627
|
const keyToUse = privateKey || globalKeyPair?.privateKey;
|
|
276
628
|
if (keyToUse) {
|
|
277
629
|
try {
|
|
278
|
-
decryptedStr = await decryptValue(encryptedStr, keyToUse);
|
|
279
|
-
} catch {
|
|
280
|
-
const { base64Decode: base64Decode2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
|
|
630
|
+
decryptedStr = await decryptValue(encryptedStr, keyToUse, globalHMACKey);
|
|
631
|
+
} catch (error) {
|
|
281
632
|
try {
|
|
282
|
-
decryptedStr =
|
|
633
|
+
decryptedStr = await handleDecryptError(error, encryptedStr, key);
|
|
283
634
|
} catch {
|
|
284
635
|
return defaultValue;
|
|
285
636
|
}
|
|
286
637
|
}
|
|
287
638
|
} else {
|
|
288
|
-
const { base64Decode: base64Decode2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
|
|
289
639
|
try {
|
|
290
|
-
decryptedStr =
|
|
640
|
+
decryptedStr = handleNoKeyDecode(encryptedStr, key);
|
|
291
641
|
} catch {
|
|
292
642
|
return defaultValue;
|
|
293
643
|
}
|
|
294
644
|
}
|
|
295
|
-
const item =
|
|
645
|
+
const item = safeParseJSON(decryptedStr, "object");
|
|
296
646
|
if (item.expiry && Date.now() > item.expiry) {
|
|
297
647
|
window.localStorage.removeItem(key);
|
|
298
648
|
return defaultValue;
|
|
299
649
|
}
|
|
300
650
|
return item.value;
|
|
301
|
-
} catch {
|
|
651
|
+
} catch (error) {
|
|
652
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
653
|
+
console.warn(`Failed to parse storage item for key "${key}"`);
|
|
654
|
+
}
|
|
302
655
|
return defaultValue;
|
|
303
656
|
}
|
|
304
657
|
},
|
|
@@ -311,7 +664,9 @@ var localStorage = {
|
|
|
311
664
|
try {
|
|
312
665
|
window.localStorage.removeItem(key);
|
|
313
666
|
} catch (error) {
|
|
314
|
-
|
|
667
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
668
|
+
console.error("localStorage.remove error");
|
|
669
|
+
}
|
|
315
670
|
}
|
|
316
671
|
},
|
|
317
672
|
/**
|
|
@@ -322,7 +677,9 @@ var localStorage = {
|
|
|
322
677
|
try {
|
|
323
678
|
window.localStorage.clear();
|
|
324
679
|
} catch (error) {
|
|
325
|
-
|
|
680
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
681
|
+
console.error("localStorage.clear error");
|
|
682
|
+
}
|
|
326
683
|
}
|
|
327
684
|
},
|
|
328
685
|
/**
|
|
@@ -338,7 +695,9 @@ var localStorage = {
|
|
|
338
695
|
if (key) keys.push(key);
|
|
339
696
|
}
|
|
340
697
|
} catch (error) {
|
|
341
|
-
|
|
698
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
699
|
+
console.error("localStorage.keys error");
|
|
700
|
+
}
|
|
342
701
|
}
|
|
343
702
|
return keys;
|
|
344
703
|
}
|
|
@@ -358,17 +717,24 @@ var sessionStorage = {
|
|
|
358
717
|
const { publicKey } = options;
|
|
359
718
|
try {
|
|
360
719
|
const jsonStr = JSON.stringify(value);
|
|
720
|
+
await ensureKeyPair();
|
|
361
721
|
const keyToUse = publicKey || globalKeyPair?.publicKey;
|
|
362
722
|
if (keyToUse) {
|
|
363
|
-
const encrypted = await encryptValue(jsonStr, keyToUse);
|
|
723
|
+
const encrypted = await encryptValue(jsonStr, keyToUse, globalHMACKey);
|
|
364
724
|
window.sessionStorage.setItem(key, encrypted);
|
|
365
725
|
} else {
|
|
366
|
-
|
|
367
|
-
|
|
726
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
727
|
+
console.warn(
|
|
728
|
+
`\u26A0\uFE0F SECURITY WARNING: Storing data without encryption for key "${key}". Data is only Base64 encoded, not encrypted!`
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
const encoded = base64Encode(jsonStr);
|
|
368
732
|
window.sessionStorage.setItem(key, encoded);
|
|
369
733
|
}
|
|
370
734
|
} catch (error) {
|
|
371
|
-
|
|
735
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
736
|
+
console.error("sessionStorage.set error:", error);
|
|
737
|
+
}
|
|
372
738
|
throw error;
|
|
373
739
|
}
|
|
374
740
|
},
|
|
@@ -386,28 +752,27 @@ var sessionStorage = {
|
|
|
386
752
|
try {
|
|
387
753
|
const encryptedStr = window.sessionStorage.getItem(key);
|
|
388
754
|
if (!encryptedStr) return defaultValue;
|
|
755
|
+
await ensureKeyPair();
|
|
389
756
|
let decryptedStr;
|
|
390
757
|
const keyToUse = privateKey || globalKeyPair?.privateKey;
|
|
391
758
|
if (keyToUse) {
|
|
392
759
|
try {
|
|
393
|
-
decryptedStr = await decryptValue(encryptedStr, keyToUse);
|
|
394
|
-
} catch {
|
|
395
|
-
const { base64Decode: base64Decode2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
|
|
760
|
+
decryptedStr = await decryptValue(encryptedStr, keyToUse, globalHMACKey);
|
|
761
|
+
} catch (error) {
|
|
396
762
|
try {
|
|
397
|
-
decryptedStr =
|
|
763
|
+
decryptedStr = await handleDecryptError(error, encryptedStr, key);
|
|
398
764
|
} catch {
|
|
399
765
|
return defaultValue;
|
|
400
766
|
}
|
|
401
767
|
}
|
|
402
768
|
} else {
|
|
403
|
-
const { base64Decode: base64Decode2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
|
|
404
769
|
try {
|
|
405
|
-
decryptedStr =
|
|
770
|
+
decryptedStr = handleNoKeyDecode(encryptedStr, key);
|
|
406
771
|
} catch {
|
|
407
772
|
return defaultValue;
|
|
408
773
|
}
|
|
409
774
|
}
|
|
410
|
-
return
|
|
775
|
+
return safeParseJSON(decryptedStr);
|
|
411
776
|
} catch {
|
|
412
777
|
return defaultValue;
|
|
413
778
|
}
|
|
@@ -421,7 +786,9 @@ var sessionStorage = {
|
|
|
421
786
|
try {
|
|
422
787
|
window.sessionStorage.removeItem(key);
|
|
423
788
|
} catch (error) {
|
|
424
|
-
|
|
789
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
790
|
+
console.error("sessionStorage.remove error");
|
|
791
|
+
}
|
|
425
792
|
}
|
|
426
793
|
},
|
|
427
794
|
/**
|
|
@@ -432,7 +799,9 @@ var sessionStorage = {
|
|
|
432
799
|
try {
|
|
433
800
|
window.sessionStorage.clear();
|
|
434
801
|
} catch (error) {
|
|
435
|
-
|
|
802
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.error) {
|
|
803
|
+
console.error("sessionStorage.clear error");
|
|
804
|
+
}
|
|
436
805
|
}
|
|
437
806
|
}
|
|
438
807
|
};
|
|
@@ -467,10 +836,15 @@ var cookie = {
|
|
|
467
836
|
if (typeof document === "undefined") return void 0;
|
|
468
837
|
const name = encodeURIComponent(key);
|
|
469
838
|
const cookies = document.cookie.split(";");
|
|
470
|
-
for (const
|
|
471
|
-
const
|
|
839
|
+
for (const cookieStr of cookies) {
|
|
840
|
+
const trimmed = cookieStr.trim();
|
|
841
|
+
const eqIndex = trimmed.indexOf("=");
|
|
842
|
+
if (eqIndex === -1) continue;
|
|
843
|
+
const cookieKey = trimmed.substring(0, eqIndex).trim();
|
|
844
|
+
const cookieValue = trimmed.substring(eqIndex + 1).trim();
|
|
472
845
|
if (cookieKey === name) {
|
|
473
|
-
|
|
846
|
+
const decoded = decodeURIComponent(cookieValue);
|
|
847
|
+
return decoded === "" ? void 0 : decoded;
|
|
474
848
|
}
|
|
475
849
|
}
|
|
476
850
|
return void 0;
|
|
@@ -494,15 +868,24 @@ var cookie = {
|
|
|
494
868
|
if (typeof document === "undefined") return {};
|
|
495
869
|
const cookies = {};
|
|
496
870
|
document.cookie.split(";").forEach((cookieStr) => {
|
|
497
|
-
const
|
|
871
|
+
const trimmed = cookieStr.trim();
|
|
872
|
+
if (!trimmed) return;
|
|
873
|
+
const eqIndex = trimmed.indexOf("=");
|
|
874
|
+
if (eqIndex === -1) return;
|
|
875
|
+
const key = trimmed.substring(0, eqIndex).trim();
|
|
876
|
+
const value = trimmed.substring(eqIndex + 1).trim();
|
|
498
877
|
if (key && value) {
|
|
499
|
-
|
|
878
|
+
const decodedKey = decodeURIComponent(key);
|
|
879
|
+
const decodedValue = decodeURIComponent(value);
|
|
880
|
+
if (decodedValue !== "") {
|
|
881
|
+
cookies[decodedKey] = decodedValue;
|
|
882
|
+
}
|
|
500
883
|
}
|
|
501
884
|
});
|
|
502
885
|
return cookies;
|
|
503
886
|
}
|
|
504
887
|
};
|
|
505
|
-
var
|
|
888
|
+
var secureStorage = {
|
|
506
889
|
/**
|
|
507
890
|
* 设置值(优先使用localStorage,失败则使用sessionStorage)
|
|
508
891
|
* @param key - 键
|
|
@@ -517,7 +900,9 @@ var storage = {
|
|
|
517
900
|
try {
|
|
518
901
|
await sessionStorage.set(key, value, { publicKey: options.publicKey });
|
|
519
902
|
} catch {
|
|
520
|
-
|
|
903
|
+
if (!initOptions.isProduction && typeof console !== "undefined" && console.warn) {
|
|
904
|
+
console.warn("Both localStorage and sessionStorage are not available");
|
|
905
|
+
}
|
|
521
906
|
}
|
|
522
907
|
}
|
|
523
908
|
},
|
|
@@ -547,7 +932,25 @@ var storage = {
|
|
|
547
932
|
clear() {
|
|
548
933
|
localStorage.clear();
|
|
549
934
|
sessionStorage.clear();
|
|
935
|
+
},
|
|
936
|
+
/**
|
|
937
|
+
* 获取所有键名
|
|
938
|
+
* @returns 键名数组
|
|
939
|
+
*/
|
|
940
|
+
keys() {
|
|
941
|
+
const localKeys = localStorage.keys();
|
|
942
|
+
const sessionKeys = [];
|
|
943
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
944
|
+
try {
|
|
945
|
+
for (let i = 0; i < window.sessionStorage.length; i++) {
|
|
946
|
+
const key = window.sessionStorage.key(i);
|
|
947
|
+
if (key) sessionKeys.push(key);
|
|
948
|
+
}
|
|
949
|
+
} catch (error) {
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
return Array.from(/* @__PURE__ */ new Set([...localKeys, ...sessionKeys]));
|
|
550
953
|
}
|
|
551
954
|
};
|
|
552
955
|
|
|
553
|
-
export { cookie, getStorageKeyPair,
|
|
956
|
+
export { clearPersistedKeys, cookie, getKeyUsageCount, getStorageKeyPair, initializeStorageKeys, resetKeyUsageCount, secureStorage, setStorageKeyPair };
|