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