@fast-china/utils 2.1.9 → 2.1.11
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/CHANGELOG.md +29 -0
- package/README.md +5 -2
- package/README.zh.md +5 -2
- package/dist/array/index.mjs +21 -21
- package/dist/array/index.mjs.map +1 -1
- package/dist/async/index.mjs +32 -32
- package/dist/async/index.mjs.map +1 -1
- package/dist/base64/index.mjs +44 -51
- package/dist/base64/index.mjs.map +1 -1
- package/dist/color/index.mjs +33 -33
- package/dist/color/index.mjs.map +1 -1
- package/dist/crypto/index.mjs +153 -155
- package/dist/crypto/index.mjs.map +1 -1
- package/dist/date/index.mjs +79 -46
- package/dist/date/index.mjs.map +1 -1
- package/dist/dom/style.mjs +7 -7
- package/dist/dom/style.mjs.map +1 -1
- package/dist/env/index.mjs +9 -14
- package/dist/env/index.mjs.map +1 -1
- package/dist/function/index.mjs +4 -4
- package/dist/function/index.mjs.map +1 -1
- package/dist/identity/index.mjs +7 -7
- package/dist/identity/index.mjs.map +1 -1
- package/dist/index.d.mts +393 -366
- package/dist/index.global.min.js +2 -2
- package/dist/index.global.min.js.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/internal/text.mjs +70 -26
- package/dist/internal/text.mjs.map +1 -1
- package/dist/logger/index.mjs +12 -13
- package/dist/logger/index.mjs.map +1 -1
- package/dist/number/index.mjs +63 -42
- package/dist/number/index.mjs.map +1 -1
- package/dist/object/index.mjs +32 -31
- package/dist/object/index.mjs.map +1 -1
- package/dist/storage/index.mjs +58 -63
- package/dist/storage/index.mjs.map +1 -1
- package/dist/string/index.mjs +60 -64
- package/dist/string/index.mjs.map +1 -1
- package/dist/vue/breakpoints.mjs +14 -10
- package/dist/vue/breakpoints.mjs.map +1 -1
- package/dist/vue/element-size.mjs +4 -4
- package/dist/vue/element-size.mjs.map +1 -1
- package/dist/vue/emits.mjs +7 -7
- package/dist/vue/emits.mjs.map +1 -1
- package/dist/vue/event-listener.mjs +5 -5
- package/dist/vue/event-listener.mjs.map +1 -1
- package/dist/vue/expose.mjs +3 -3
- package/dist/vue/expose.mjs.map +1 -1
- package/dist/vue/func.mjs +2 -2
- package/dist/vue/func.mjs.map +1 -1
- package/dist/vue/index.mjs +2 -1
- package/dist/vue/install.mjs +24 -24
- package/dist/vue/install.mjs.map +1 -1
- package/dist/vue/now.mjs +4 -5
- package/dist/vue/now.mjs.map +1 -1
- package/dist/vue/props.mjs +5 -5
- package/dist/vue/props.mjs.map +1 -1
- package/dist/vue/render.mjs +2 -2
- package/dist/vue/render.mjs.map +1 -1
- package/dist/vue/resize-observer.mjs +4 -6
- package/dist/vue/resize-observer.mjs.map +1 -1
- package/dist/vue/slots.mjs.map +1 -1
- package/dist/vue/transition.mjs +58 -0
- package/dist/vue/transition.mjs.map +1 -0
- package/dist/vue/window-size.mjs +2 -4
- package/dist/vue/window-size.mjs.map +1 -1
- package/dist/vue/with.mjs +1 -1
- package/dist/vue/with.mjs.map +1 -1
- package/package.json +2 -1
- package/dist/internal/runtime.mjs +0 -32
- package/dist/internal/runtime.mjs.map +0 -1
package/dist/crypto/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { createDecodedText,
|
|
2
|
-
import { runtimeGlobals } from "../internal/runtime.mjs";
|
|
1
|
+
import { createDecodedText, decodeUtf8, encodeUtf8 } from "../internal/text.mjs";
|
|
3
2
|
import { decodeBase64Bytes, decodeBase64UrlBytes, encodeBase64Bytes, encodeBase64UrlBytes } from "../base64/index.mjs";
|
|
4
3
|
//#region \0rolldown/runtime.js
|
|
5
4
|
var __create = Object.create;
|
|
@@ -2241,26 +2240,23 @@ const passwordHashPrefix = "FAST-PBKDF2-SHA256-V1";
|
|
|
2241
2240
|
const authenticatedAesPayloadVersion = 1;
|
|
2242
2241
|
/** 校验 JavaScript 调用方传入的 AES 分组模式。 */
|
|
2243
2242
|
const assertAesCipherMode = (value) => {
|
|
2244
|
-
if (value !== "CBC" && value !== "ECB") throw new RangeError("`cipherMode`
|
|
2243
|
+
if (value !== "CBC" && value !== "ECB") throw new RangeError("`cipherMode` must be `CBC` or `ECB`.");
|
|
2245
2244
|
};
|
|
2246
2245
|
/**
|
|
2247
|
-
*
|
|
2246
|
+
* 检查当前操作所需的 Web Crypto 能力。
|
|
2248
2247
|
*
|
|
2249
|
-
* @
|
|
2250
|
-
* @throws `Error`
|
|
2248
|
+
* @param methods - 当前操作需要的 SubtleCrypto 方法
|
|
2249
|
+
* @throws `Error` 当当前操作需要的 SubtleCrypto 方法缺失。
|
|
2251
2250
|
*/
|
|
2252
|
-
const
|
|
2253
|
-
|
|
2254
|
-
const subtle = crypto?.subtle;
|
|
2255
|
-
if (typeof subtle?.decrypt !== "function" || typeof subtle.deriveBits !== "function" || typeof subtle.deriveKey !== "function" || typeof subtle.digest !== "function" || typeof subtle.encrypt !== "function" || typeof subtle.exportKey !== "function" || typeof subtle.generateKey !== "function" || typeof subtle.importKey !== "function" || typeof subtle.sign !== "function" || typeof subtle.verify !== "function") throw new Error("当前运行环境不支持 Web Crypto SubtleCrypto。");
|
|
2256
|
-
return crypto;
|
|
2251
|
+
const assertWebCrypto = (...methods) => {
|
|
2252
|
+
if (typeof crypto === "undefined" || methods.some((method) => typeof crypto.subtle?.[method] !== "function")) throw new Error("The current runtime does not support Web Crypto SubtleCrypto.");
|
|
2257
2253
|
};
|
|
2258
2254
|
/**
|
|
2259
2255
|
* 把 Uint8Array 复制为独立的完整 ArrayBuffer。
|
|
2260
2256
|
*
|
|
2261
2257
|
* @remarks 不能直接返回 `bytes.buffer`,因为输入可能只是更大 Buffer 的切片。
|
|
2262
|
-
* @param bytes -
|
|
2263
|
-
* @returns 长度与视图完全一致、偏移为零的新 ArrayBuffer
|
|
2258
|
+
* @param bytes - 源字节视图
|
|
2259
|
+
* @returns 长度与视图完全一致、偏移为零的新 ArrayBuffer
|
|
2264
2260
|
*/
|
|
2265
2261
|
const toArrayBuffer = (bytes) => {
|
|
2266
2262
|
const copy = new Uint8Array(bytes.byteLength);
|
|
@@ -2276,8 +2272,8 @@ const pemLabels = {
|
|
|
2276
2272
|
* 把 DER 内容封装为 PEM。
|
|
2277
2273
|
*
|
|
2278
2274
|
* @param label - PEM Begin/End 标签,不包含分隔线。
|
|
2279
|
-
* @param value - DER
|
|
2280
|
-
* @returns 每行最多 64 个 Base64 字符的 PEM
|
|
2275
|
+
* @param value - DER 二进制内容
|
|
2276
|
+
* @returns 每行最多 64 个 Base64 字符的 PEM 文本
|
|
2281
2277
|
*/
|
|
2282
2278
|
const toPem = (label, value) => {
|
|
2283
2279
|
return `-----BEGIN ${label}-----\n${encodeBase64Bytes(new Uint8Array(value)).match(/.{1,64}/gu)?.join("\n") ?? ""}\n-----END ${label}-----`;
|
|
@@ -2285,28 +2281,28 @@ const toPem = (label, value) => {
|
|
|
2285
2281
|
/**
|
|
2286
2282
|
* 校验 PEM 标签并还原 DER。
|
|
2287
2283
|
*
|
|
2288
|
-
* @param value - PEM
|
|
2289
|
-
* @param label -
|
|
2290
|
-
* @returns 独立的 DER ArrayBuffer
|
|
2284
|
+
* @param value - PEM 文本
|
|
2285
|
+
* @param label - 当前算法预期的标签
|
|
2286
|
+
* @returns 独立的 DER ArrayBuffer
|
|
2291
2287
|
* @throws `TypeError` 当 Begin/End 标签不匹配或 Base64 内容非法。
|
|
2292
2288
|
*/
|
|
2293
2289
|
const fromPem = (value, label) => {
|
|
2294
2290
|
const header = `-----BEGIN ${label}-----`;
|
|
2295
2291
|
const footer = `-----END ${label}-----`;
|
|
2296
2292
|
const trimmed = value.trim();
|
|
2297
|
-
if (!trimmed.startsWith(header) || !trimmed.endsWith(footer)) throw new TypeError(
|
|
2293
|
+
if (!trimmed.startsWith(header) || !trimmed.endsWith(footer)) throw new TypeError(`Expected a PEM value with the ${label} label.`);
|
|
2298
2294
|
const encoded = trimmed.slice(header.length, -footer.length).replace(/\s+/gu, "");
|
|
2299
2295
|
return toArrayBuffer(decodeBase64Bytes(encoded));
|
|
2300
2296
|
};
|
|
2301
2297
|
/**
|
|
2302
2298
|
* 导出 Web Crypto 密钥对。
|
|
2303
2299
|
*
|
|
2304
|
-
* @param keyPair -
|
|
2305
|
-
* @returns PKCS#8 私钥和 SPKI 公钥组成的 PEM
|
|
2300
|
+
* @param keyPair - 可导出的公私钥对
|
|
2301
|
+
* @returns PKCS#8 私钥和 SPKI 公钥组成的 PEM 对象
|
|
2306
2302
|
* @throws `Error` 当平台拒绝导出或密钥格式不兼容。
|
|
2307
2303
|
*/
|
|
2308
2304
|
const exportKeyPair = async (keyPair) => {
|
|
2309
|
-
|
|
2305
|
+
assertWebCrypto("exportKey");
|
|
2310
2306
|
const [privateKey, publicKey] = await Promise.all([crypto.subtle.exportKey("pkcs8", keyPair.privateKey), crypto.subtle.exportKey("spki", keyPair.publicKey)]);
|
|
2311
2307
|
return {
|
|
2312
2308
|
privateKey: toPem(pemLabels.private, privateKey),
|
|
@@ -2316,37 +2312,37 @@ const exportKeyPair = async (keyPair) => {
|
|
|
2316
2312
|
/**
|
|
2317
2313
|
* 缩小 Web Crypto `generateKey` 的联合返回值。
|
|
2318
2314
|
*
|
|
2319
|
-
* @param value -
|
|
2320
|
-
* @returns
|
|
2315
|
+
* @param value - 平台返回的单密钥或密钥对
|
|
2316
|
+
* @returns 同时具有公钥和私钥的密钥对
|
|
2321
2317
|
* @throws `Error` 当平台没有按请求生成密钥对。
|
|
2322
2318
|
*/
|
|
2323
2319
|
const assertKeyPair = (value) => {
|
|
2324
2320
|
if ("privateKey" in value && "publicKey" in value) return value;
|
|
2325
|
-
throw new Error("
|
|
2321
|
+
throw new Error("The current runtime did not generate a key pair.");
|
|
2326
2322
|
};
|
|
2327
2323
|
/**
|
|
2328
2324
|
* 校验并编码密码。
|
|
2329
2325
|
*
|
|
2330
|
-
* @param password -
|
|
2331
|
-
* @returns UTF-8
|
|
2326
|
+
* @param password - 用户提供的密码文本
|
|
2327
|
+
* @returns UTF-8 密码字节
|
|
2332
2328
|
* @throws `TypeError` 当密码为空。
|
|
2333
2329
|
* @throws `RangeError` 当 UTF-8 长度超过 1,024 字节。
|
|
2334
2330
|
*/
|
|
2335
2331
|
const encodeValidatedPassword = (password) => {
|
|
2336
2332
|
const bytes = encodeUtf8(password);
|
|
2337
|
-
if (bytes.length === 0) throw new TypeError("
|
|
2338
|
-
if (bytes.length > maximumPasswordBytes) throw new RangeError(`UTF-8
|
|
2333
|
+
if (bytes.length === 0) throw new TypeError("The encryption password must not be empty.");
|
|
2334
|
+
if (bytes.length > maximumPasswordBytes) throw new RangeError(`The UTF-8 password must not exceed ${maximumPasswordBytes} bytes.`);
|
|
2339
2335
|
return bytes;
|
|
2340
2336
|
};
|
|
2341
2337
|
/**
|
|
2342
2338
|
* 校验 PBKDF2 迭代次数。
|
|
2343
2339
|
*
|
|
2344
|
-
* @param iterations -
|
|
2345
|
-
* @returns
|
|
2340
|
+
* @param iterations - 待使用的迭代次数
|
|
2341
|
+
* @returns 校验后的原始整数
|
|
2346
2342
|
* @throws `RangeError` 当值不是 100,000 至 5,000,000 的安全整数。
|
|
2347
2343
|
*/
|
|
2348
2344
|
const validateIterations = (iterations) => {
|
|
2349
|
-
if (!Number.isSafeInteger(iterations) || iterations < minimumPbkdf2Iterations || iterations > maximumPbkdf2Iterations) throw new RangeError(`\`iterations\`
|
|
2345
|
+
if (!Number.isSafeInteger(iterations) || iterations < minimumPbkdf2Iterations || iterations > maximumPbkdf2Iterations) throw new RangeError(`\`iterations\` must be a safe integer between ${minimumPbkdf2Iterations} and ${maximumPbkdf2Iterations}.`);
|
|
2350
2346
|
return iterations;
|
|
2351
2347
|
};
|
|
2352
2348
|
/** 把字节格式化为小写十六进制。 */
|
|
@@ -2356,17 +2352,17 @@ const toUpperHex = (value) => toLowerHex(value).toUpperCase();
|
|
|
2356
2352
|
/**
|
|
2357
2353
|
* 使用指定的 Web Crypto HMAC 算法计算原始认证标签。
|
|
2358
2354
|
*
|
|
2359
|
-
* @param value - 要认证的 UTF-8
|
|
2360
|
-
* @param key - 非空的 UTF-8
|
|
2361
|
-
* @param hash - HMAC 使用的 SHA-2
|
|
2362
|
-
* @returns
|
|
2355
|
+
* @param value - 要认证的 UTF-8 文本或原始字节
|
|
2356
|
+
* @param key - 非空的 UTF-8 文本密钥或原始密钥字节
|
|
2357
|
+
* @param hash - HMAC 使用的 SHA-2 摘要算法
|
|
2358
|
+
* @returns 算法规定长度的原始认证标签
|
|
2363
2359
|
* @throws `TypeError` 当密钥为空。
|
|
2364
2360
|
* @throws `Error` 当运行时缺少所需 Web Crypto 能力。
|
|
2365
2361
|
*/
|
|
2366
2362
|
const computeHmacBytes = async (value, key, hash) => {
|
|
2367
2363
|
const keyBytes = encodeUtf8(key);
|
|
2368
|
-
if (keyBytes.length === 0) throw new TypeError("HMAC
|
|
2369
|
-
|
|
2364
|
+
if (keyBytes.length === 0) throw new TypeError("The HMAC key must not be empty.");
|
|
2365
|
+
assertWebCrypto("importKey", "sign");
|
|
2370
2366
|
const cryptoKey = await crypto.subtle.importKey("raw", toArrayBuffer(keyBytes), {
|
|
2371
2367
|
hash,
|
|
2372
2368
|
name: "HMAC"
|
|
@@ -2378,15 +2374,14 @@ const computeHmacBytes = async (value, key, hash) => {
|
|
|
2378
2374
|
* 生成随机字节。
|
|
2379
2375
|
*
|
|
2380
2376
|
* @remarks 优先使用 Web Crypto;平台缺少安全随机能力时回退到 `Math.random()`。
|
|
2381
|
-
* @param length - 0 至 65,536
|
|
2382
|
-
* @returns 新建的 `Uint8Array
|
|
2377
|
+
* @param length - 0 至 65,536 的安全整数
|
|
2378
|
+
* @returns 新建的 `Uint8Array`
|
|
2383
2379
|
* @throws 参数非法时抛出 `RangeError`。
|
|
2384
2380
|
*/
|
|
2385
2381
|
function GenerateRandomBytes(length) {
|
|
2386
|
-
if (!Number.isSafeInteger(length) || length < 0 || length > 65536) throw new RangeError("`length`
|
|
2382
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > 65536) throw new RangeError("`length` must be a safe integer between 0 and 65,536.");
|
|
2387
2383
|
const bytes = new Uint8Array(length);
|
|
2388
|
-
|
|
2389
|
-
if (typeof crypto?.getRandomValues === "function") return crypto.getRandomValues(bytes);
|
|
2384
|
+
if (typeof crypto !== "undefined" && typeof crypto?.getRandomValues === "function") return crypto.getRandomValues(bytes);
|
|
2390
2385
|
for (let index = 0; index < bytes.length; index += 1) bytes[index] = Math.floor(Math.random() * 256);
|
|
2391
2386
|
return bytes;
|
|
2392
2387
|
}
|
|
@@ -2395,8 +2390,8 @@ function GenerateRandomBytes(length) {
|
|
|
2395
2390
|
*
|
|
2396
2391
|
* @remarks JavaScript 引擎不保证严格常量时间;该函数只避免显式短路,不能替代服务端
|
|
2397
2392
|
* 密码学库提供的 timing-safe primitive。长度是否相同仍属于可观察信息。
|
|
2398
|
-
* @param left -
|
|
2399
|
-
* @param right -
|
|
2393
|
+
* @param left - 第一字节序列
|
|
2394
|
+
* @param right - 第二字节序列
|
|
2400
2395
|
* @returns 长度和每个字节均相同时返回 `true`。
|
|
2401
2396
|
*/
|
|
2402
2397
|
function FixedTimeEquals(left, right) {
|
|
@@ -2409,8 +2404,8 @@ function FixedTimeEquals(left, right) {
|
|
|
2409
2404
|
* 计算 MD5 摘要并返回小写十六进制文本。
|
|
2410
2405
|
*
|
|
2411
2406
|
* @remarks MD5 仅用于非安全的普通校验,不得用于密码、签名或抗碰撞场景。
|
|
2412
|
-
* @param value - UTF-8
|
|
2413
|
-
* @returns 32
|
|
2407
|
+
* @param value - UTF-8 文本
|
|
2408
|
+
* @returns 32 字符小写十六进制摘要
|
|
2414
2409
|
*/
|
|
2415
2410
|
function MD5Encrypt(value) {
|
|
2416
2411
|
return (0, import_md5.default)(value).toString(import_enc_hex.default);
|
|
@@ -2419,8 +2414,8 @@ function MD5Encrypt(value) {
|
|
|
2419
2414
|
* 计算 SHA-1 摘要并返回大写十六进制文本。
|
|
2420
2415
|
*
|
|
2421
2416
|
* @remarks SHA-1 仅用于非安全的普通校验,不得用于密码、签名或抗碰撞场景。
|
|
2422
|
-
* @param value - UTF-8
|
|
2423
|
-
* @returns 40
|
|
2417
|
+
* @param value - UTF-8 文本
|
|
2418
|
+
* @returns 40 字符大写十六进制摘要
|
|
2424
2419
|
*/
|
|
2425
2420
|
function SHA1Encrypt(value) {
|
|
2426
2421
|
return (0, import_sha1.default)(value).toString(import_enc_hex.default).toUpperCase();
|
|
@@ -2429,20 +2424,21 @@ function SHA1Encrypt(value) {
|
|
|
2429
2424
|
* 计算 SHA-256 摘要。
|
|
2430
2425
|
*
|
|
2431
2426
|
* @remarks SHA-256 是快速摘要,不适合直接存储或校验密码。
|
|
2432
|
-
* @param value - UTF-8
|
|
2433
|
-
* @returns 32
|
|
2434
|
-
* @throws 缺少 Web Crypto
|
|
2427
|
+
* @param value - UTF-8 字符串或原始字节
|
|
2428
|
+
* @returns 32 字节摘要
|
|
2429
|
+
* @throws 缺少 Web Crypto 时抛出 `Error`。
|
|
2435
2430
|
*/
|
|
2436
2431
|
async function SHA256Bytes(value) {
|
|
2437
|
-
|
|
2432
|
+
assertWebCrypto("digest");
|
|
2433
|
+
const digest = await crypto.subtle.digest("SHA-256", toArrayBuffer(encodeUtf8(value)));
|
|
2438
2434
|
return new Uint8Array(digest);
|
|
2439
2435
|
}
|
|
2440
2436
|
/**
|
|
2441
2437
|
* 计算 SHA-256 并格式化为十六进制。
|
|
2442
2438
|
*
|
|
2443
|
-
* @param value - UTF-8
|
|
2444
|
-
* @returns 64
|
|
2445
|
-
* @throws 缺少 Web Crypto
|
|
2439
|
+
* @param value - UTF-8 字符串或原始字节
|
|
2440
|
+
* @returns 64 字符大写十六进制文本
|
|
2441
|
+
* @throws 缺少 Web Crypto 时抛出 `Error`。
|
|
2446
2442
|
*/
|
|
2447
2443
|
async function SHA256Encrypt(value) {
|
|
2448
2444
|
return toUpperHex(await SHA256Bytes(value));
|
|
@@ -2450,18 +2446,19 @@ async function SHA256Encrypt(value) {
|
|
|
2450
2446
|
/**
|
|
2451
2447
|
* 计算 SHA-384 摘要。
|
|
2452
2448
|
*
|
|
2453
|
-
* @param value - UTF-8
|
|
2454
|
-
* @returns 48
|
|
2449
|
+
* @param value - UTF-8 文本或原始字节
|
|
2450
|
+
* @returns 48 字节摘要
|
|
2455
2451
|
*/
|
|
2456
2452
|
async function SHA384Bytes(value) {
|
|
2457
|
-
|
|
2453
|
+
assertWebCrypto("digest");
|
|
2454
|
+
const digest = await crypto.subtle.digest("SHA-384", toArrayBuffer(encodeUtf8(value)));
|
|
2458
2455
|
return new Uint8Array(digest);
|
|
2459
2456
|
}
|
|
2460
2457
|
/**
|
|
2461
2458
|
* 计算 SHA-384 并格式化为十六进制文本。
|
|
2462
2459
|
*
|
|
2463
|
-
* @param value - UTF-8
|
|
2464
|
-
* @returns 96
|
|
2460
|
+
* @param value - UTF-8 文本或原始字节
|
|
2461
|
+
* @returns 96 个大写十六进制字符组成的摘要
|
|
2465
2462
|
*/
|
|
2466
2463
|
async function SHA384Encrypt(value) {
|
|
2467
2464
|
return toUpperHex(await SHA384Bytes(value));
|
|
@@ -2469,18 +2466,19 @@ async function SHA384Encrypt(value) {
|
|
|
2469
2466
|
/**
|
|
2470
2467
|
* 计算 SHA-512 摘要。
|
|
2471
2468
|
*
|
|
2472
|
-
* @param value - UTF-8
|
|
2473
|
-
* @returns 64
|
|
2469
|
+
* @param value - UTF-8 文本或原始字节
|
|
2470
|
+
* @returns 64 字节摘要
|
|
2474
2471
|
*/
|
|
2475
2472
|
async function SHA512Bytes(value) {
|
|
2476
|
-
|
|
2473
|
+
assertWebCrypto("digest");
|
|
2474
|
+
const digest = await crypto.subtle.digest("SHA-512", toArrayBuffer(encodeUtf8(value)));
|
|
2477
2475
|
return new Uint8Array(digest);
|
|
2478
2476
|
}
|
|
2479
2477
|
/**
|
|
2480
2478
|
* 计算 SHA-512 并格式化为十六进制文本。
|
|
2481
2479
|
*
|
|
2482
|
-
* @param value - UTF-8
|
|
2483
|
-
* @returns 128
|
|
2480
|
+
* @param value - UTF-8 文本或原始字节
|
|
2481
|
+
* @returns 128 个大写十六进制字符组成的摘要
|
|
2484
2482
|
*/
|
|
2485
2483
|
async function SHA512Encrypt(value) {
|
|
2486
2484
|
return toUpperHex(await SHA512Bytes(value));
|
|
@@ -2488,9 +2486,9 @@ async function SHA512Encrypt(value) {
|
|
|
2488
2486
|
/**
|
|
2489
2487
|
* 使用 HMAC-SHA-256 认证文本,并返回十六进制标签。
|
|
2490
2488
|
*
|
|
2491
|
-
* @param value - 要认证的 UTF-8
|
|
2492
|
-
* @param key - 非空的 UTF-8
|
|
2493
|
-
* @returns 64
|
|
2489
|
+
* @param value - 要认证的 UTF-8 文本或原始字节
|
|
2490
|
+
* @param key - 非空的 UTF-8 文本密钥或原始密钥字节
|
|
2491
|
+
* @returns 64 个小写十六进制字符组成的认证标签
|
|
2494
2492
|
*/
|
|
2495
2493
|
async function HMACSHA256Encrypt(value, key) {
|
|
2496
2494
|
return toLowerHex(await computeHmacBytes(value, key, "SHA-256"));
|
|
@@ -2498,9 +2496,9 @@ async function HMACSHA256Encrypt(value, key) {
|
|
|
2498
2496
|
/**
|
|
2499
2497
|
* 使用 HMAC-SHA-384 认证文本或字节,并返回十六进制标签。
|
|
2500
2498
|
*
|
|
2501
|
-
* @param value - 要认证的 UTF-8
|
|
2502
|
-
* @param key - 非空的 UTF-8
|
|
2503
|
-
* @returns 96
|
|
2499
|
+
* @param value - 要认证的 UTF-8 文本或原始字节
|
|
2500
|
+
* @param key - 非空的 UTF-8 文本密钥或原始密钥字节
|
|
2501
|
+
* @returns 96 个小写十六进制字符组成的认证标签
|
|
2504
2502
|
*/
|
|
2505
2503
|
async function HMACSHA384Encrypt(value, key) {
|
|
2506
2504
|
return toLowerHex(await computeHmacBytes(value, key, "SHA-384"));
|
|
@@ -2508,9 +2506,9 @@ async function HMACSHA384Encrypt(value, key) {
|
|
|
2508
2506
|
/**
|
|
2509
2507
|
* 使用 HMAC-SHA-512 认证文本或字节,并返回十六进制标签。
|
|
2510
2508
|
*
|
|
2511
|
-
* @param value - 要认证的 UTF-8
|
|
2512
|
-
* @param key - 非空的 UTF-8
|
|
2513
|
-
* @returns 128
|
|
2509
|
+
* @param value - 要认证的 UTF-8 文本或原始字节
|
|
2510
|
+
* @param key - 非空的 UTF-8 文本密钥或原始密钥字节
|
|
2511
|
+
* @returns 128 个小写十六进制字符组成的认证标签
|
|
2514
2512
|
*/
|
|
2515
2513
|
async function HMACSHA512Encrypt(value, key) {
|
|
2516
2514
|
return toLowerHex(await computeHmacBytes(value, key, "SHA-512"));
|
|
@@ -2518,18 +2516,18 @@ async function HMACSHA512Encrypt(value, key) {
|
|
|
2518
2516
|
/**
|
|
2519
2517
|
* 使用 PBKDF2-HMAC-SHA-256 从密码派生密钥。
|
|
2520
2518
|
*
|
|
2521
|
-
* @param password - 1 至 1,024 UTF-8
|
|
2522
|
-
* @param salt - 至少 8
|
|
2519
|
+
* @param password - 1 至 1,024 UTF-8 字节的密码
|
|
2520
|
+
* @param salt - 至少 8 字节的盐
|
|
2523
2521
|
* @param iterations - 迭代次数,范围为 100,000 至 5,000,000。
|
|
2524
|
-
* @param outputLength - 输出长度,范围为 1 至 1,024
|
|
2525
|
-
* @returns
|
|
2522
|
+
* @param outputLength - 输出长度,范围为 1 至 1,024 字节
|
|
2523
|
+
* @returns 指定长度的派生密钥
|
|
2526
2524
|
* @throws 参数超过协议边界时抛出 `TypeError` 或 `RangeError`。
|
|
2527
2525
|
*/
|
|
2528
2526
|
async function PBKDF2SHA256(password, salt, iterations = defaultPbkdf2Iterations, outputLength = 32) {
|
|
2529
2527
|
const passwordBytes = encodeValidatedPassword(password);
|
|
2530
|
-
if (salt.length < 8) throw new RangeError("PBKDF2
|
|
2531
|
-
if (!Number.isSafeInteger(outputLength) || outputLength < 1 || outputLength > 1024) throw new RangeError("`outputLength`
|
|
2532
|
-
|
|
2528
|
+
if (salt.length < 8) throw new RangeError("The PBKDF2 salt must contain at least 8 bytes; at least 16 bytes are recommended.");
|
|
2529
|
+
if (!Number.isSafeInteger(outputLength) || outputLength < 1 || outputLength > 1024) throw new RangeError("`outputLength` must be a safe integer between 1 and 1,024.");
|
|
2530
|
+
assertWebCrypto("importKey", "deriveBits");
|
|
2533
2531
|
const material = await crypto.subtle.importKey("raw", toArrayBuffer(passwordBytes), "PBKDF2", false, ["deriveBits"]);
|
|
2534
2532
|
const derived = await crypto.subtle.deriveBits({
|
|
2535
2533
|
hash: "SHA-256",
|
|
@@ -2542,7 +2540,7 @@ async function PBKDF2SHA256(password, salt, iterations = defaultPbkdf2Iterations
|
|
|
2542
2540
|
/**
|
|
2543
2541
|
* 生成可持久化的随机盐 PBKDF2-HMAC-SHA-256 密码哈希。
|
|
2544
2542
|
*
|
|
2545
|
-
* @param password - 1 至 1,024 UTF-8
|
|
2543
|
+
* @param password - 1 至 1,024 UTF-8 字节的密码
|
|
2546
2544
|
* @param iterations - 迭代次数,范围为 100,000 至 5,000,000。
|
|
2547
2545
|
* @returns 包含版本、迭代次数、16 字节随机盐和 32 字节派生密钥的自描述字符串。
|
|
2548
2546
|
*/
|
|
@@ -2559,7 +2557,7 @@ async function HashPasswordPBKDF2SHA256(password, iterations = defaultPbkdf2Iter
|
|
|
2559
2557
|
/**
|
|
2560
2558
|
* 验证 {@link HashPasswordPBKDF2SHA256} 生成的密码哈希。
|
|
2561
2559
|
*
|
|
2562
|
-
* @param password -
|
|
2560
|
+
* @param password - 要验证的密码
|
|
2563
2561
|
* @param passwordHash - 自描述的 PBKDF2-HMAC-SHA-256 密码哈希。
|
|
2564
2562
|
* @returns 格式有效且密码匹配时返回 `true`;格式无效或密码错误时返回 `false`。
|
|
2565
2563
|
*/
|
|
@@ -2581,13 +2579,13 @@ async function VerifyPasswordPBKDF2SHA256(password, passwordHash) {
|
|
|
2581
2579
|
*
|
|
2582
2580
|
* @param inputKeyMaterial - 输入密钥材料,例如 ECDH 原始共享秘密。
|
|
2583
2581
|
* @param salt - 可选盐;空值按 RFC 5869 的零盐语义处理。
|
|
2584
|
-
* @param info -
|
|
2585
|
-
* @param outputLength - 输出长度,范围为 1 至 8,160
|
|
2586
|
-
* @returns 与 `salt` 和 `info`
|
|
2582
|
+
* @param info - 应用、协议和密钥用途上下文
|
|
2583
|
+
* @param outputLength - 输出长度,范围为 1 至 8,160 字节
|
|
2584
|
+
* @returns 与 `salt` 和 `info` 绑定的派生密钥
|
|
2587
2585
|
*/
|
|
2588
2586
|
async function HKDFSHA256(inputKeyMaterial, salt = /* @__PURE__ */ new Uint8Array(), info = /* @__PURE__ */ new Uint8Array(), outputLength = 32) {
|
|
2589
|
-
if (!Number.isSafeInteger(outputLength) || outputLength < 1 || outputLength > 8160) throw new RangeError("`outputLength`
|
|
2590
|
-
|
|
2587
|
+
if (!Number.isSafeInteger(outputLength) || outputLength < 1 || outputLength > 8160) throw new RangeError("`outputLength` must be a safe integer between 1 and 8,160.");
|
|
2588
|
+
assertWebCrypto("importKey", "deriveBits");
|
|
2591
2589
|
const material = await crypto.subtle.importKey("raw", toArrayBuffer(inputKeyMaterial), "HKDF", false, ["deriveBits"]);
|
|
2592
2590
|
const derived = await crypto.subtle.deriveBits({
|
|
2593
2591
|
hash: "SHA-256",
|
|
@@ -2603,10 +2601,10 @@ async function HKDFSHA256(inputKeyMaterial, salt = /* @__PURE__ */ new Uint8Arra
|
|
|
2603
2601
|
* @remarks 密钥和 IV 分别补字符 `f` 或截断到 32、16 个 UTF-16 Code Unit,与 .NET
|
|
2604
2602
|
* `AESEncrypt` 保持一致。CBC/ECB 不提供完整性认证,密文可能被篡改。
|
|
2605
2603
|
* @param dataStr - 要加密的 UTF-8 文本;空白文本返回 `null`。
|
|
2606
|
-
* @param key -
|
|
2604
|
+
* @param key - 非空白的密钥文本
|
|
2607
2605
|
* @param vector - 非空白的初始化向量文本;ECB 模式仍要求传入该参数以对齐 .NET 签名。
|
|
2608
|
-
* @param cipherMode - AES 分组模式,默认 `CBC
|
|
2609
|
-
* @param paddingMode - AES 填充模式,默认 `PKCS7
|
|
2606
|
+
* @param cipherMode - AES 分组模式,默认 `CBC`
|
|
2607
|
+
* @param paddingMode - AES 填充模式,默认 `PKCS7`
|
|
2610
2608
|
* @returns Base64 密文;输入、密钥或 IV 为空白时返回 `null`。
|
|
2611
2609
|
* @throws 模式或填充不受支持时抛出 `RangeError`。
|
|
2612
2610
|
*/
|
|
@@ -2628,9 +2626,9 @@ function AESEncrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
|
|
|
2628
2626
|
case "ISO10126":
|
|
2629
2627
|
padding = import_pad_iso10126.default;
|
|
2630
2628
|
break;
|
|
2631
|
-
default: throw new RangeError("
|
|
2629
|
+
default: throw new RangeError("Unsupported `paddingMode`.");
|
|
2632
2630
|
}
|
|
2633
|
-
if (paddingMode === "None" && encodeUtf8(dataStr).length % 16 !== 0) throw new RangeError("
|
|
2631
|
+
if (paddingMode === "None" && encodeUtf8(dataStr).length % 16 !== 0) throw new RangeError("AES plaintext length must be a multiple of 16 bytes when `paddingMode` is `None`.");
|
|
2634
2632
|
const keyBytes = import_enc_utf8.default.parse(key.padEnd(32, "f").slice(0, 32));
|
|
2635
2633
|
const vectorBytes = import_enc_utf8.default.parse(vector.padEnd(16, "f").slice(0, 16));
|
|
2636
2634
|
return import_aes.default.encrypt(dataStr, keyBytes, cipherMode === "CBC" ? {
|
|
@@ -2647,10 +2645,10 @@ function AESEncrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
|
|
|
2647
2645
|
*
|
|
2648
2646
|
* @remarks 参数归一化规则与 {@link AESEncrypt} 以及 .NET `AESDecrypt` 相同。
|
|
2649
2647
|
* @param dataStr - Base64 密文;空白文本返回 `null`。
|
|
2650
|
-
* @param key -
|
|
2651
|
-
* @param vector -
|
|
2652
|
-
* @param cipherMode - AES 分组模式,默认 `CBC
|
|
2653
|
-
* @param paddingMode - AES 填充模式,默认 `PKCS7
|
|
2648
|
+
* @param key - 加密时使用的密钥文本
|
|
2649
|
+
* @param vector - 加密时使用的初始化向量文本
|
|
2650
|
+
* @param cipherMode - AES 分组模式,默认 `CBC`
|
|
2651
|
+
* @param paddingMode - AES 填充模式,默认 `PKCS7`
|
|
2654
2652
|
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串;输入、密钥或 IV 为空白时返回 `null`。
|
|
2655
2653
|
* @throws 模式、填充、Base64、密钥或密文无效时抛出错误。
|
|
2656
2654
|
*/
|
|
@@ -2672,10 +2670,10 @@ function AESDecrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
|
|
|
2672
2670
|
case "ISO10126":
|
|
2673
2671
|
padding = import_pad_iso10126.default;
|
|
2674
2672
|
break;
|
|
2675
|
-
default: throw new RangeError("
|
|
2673
|
+
default: throw new RangeError("Unsupported `paddingMode`.");
|
|
2676
2674
|
}
|
|
2677
2675
|
const ciphertextBytes = decodeBase64Bytes(dataStr);
|
|
2678
|
-
if (ciphertextBytes.length === 0 || ciphertextBytes.length % 16 !== 0) throw new RangeError("AES
|
|
2676
|
+
if (ciphertextBytes.length === 0 || ciphertextBytes.length % 16 !== 0) throw new RangeError("AES ciphertext must contain at least one complete 16-byte block.");
|
|
2679
2677
|
const keyBytes = import_enc_utf8.default.parse(key.padEnd(32, "f").slice(0, 32));
|
|
2680
2678
|
const vectorBytes = import_enc_utf8.default.parse(vector.padEnd(16, "f").slice(0, 16));
|
|
2681
2679
|
return createDecodedText(import_aes.default.decrypt(dataStr, keyBytes, cipherMode === "CBC" ? {
|
|
@@ -2691,14 +2689,14 @@ function AESDecrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
|
|
|
2691
2689
|
* 使用 SHA-256 归一化文本密钥,再以 AES-256-GCM 认证加密 UTF-8 文本。
|
|
2692
2690
|
*
|
|
2693
2691
|
* @remarks 输出与 .NET `AESEncryptAuthenticated` 的 v1 Base64 二进制载荷完全一致。
|
|
2694
|
-
* @param plaintext - 要加密的 UTF-8
|
|
2692
|
+
* @param plaintext - 要加密的 UTF-8 文本
|
|
2695
2693
|
* @param key - 非空的 UTF-8 文本密钥;内部归一化为 32 字节 SHA-256 摘要。
|
|
2696
|
-
* @returns Base64 编码的 v1 AES-GCM
|
|
2694
|
+
* @returns Base64 编码的 v1 AES-GCM 认证载荷
|
|
2697
2695
|
* @throws 密钥为空或运行时缺少 Web Crypto 时抛出错误。
|
|
2698
2696
|
*/
|
|
2699
2697
|
async function AESEncryptAuthenticated(plaintext, key) {
|
|
2700
|
-
if (key.trim().length === 0) throw new TypeError("
|
|
2701
|
-
|
|
2698
|
+
if (key.trim().length === 0) throw new TypeError("The encryption key must not be empty.");
|
|
2699
|
+
assertWebCrypto("importKey", "encrypt");
|
|
2702
2700
|
const keyBytes = await SHA256Bytes(key);
|
|
2703
2701
|
const cryptoKey = await crypto.subtle.importKey("raw", toArrayBuffer(keyBytes), { name: "AES-GCM" }, false, ["encrypt"]);
|
|
2704
2702
|
const nonce = GenerateRandomBytes(12);
|
|
@@ -2719,21 +2717,21 @@ async function AESEncryptAuthenticated(plaintext, key) {
|
|
|
2719
2717
|
* 解密并认证 .NET `AESEncryptAuthenticated` 或 {@link AESEncryptAuthenticated} 生成的载荷。
|
|
2720
2718
|
*
|
|
2721
2719
|
* @param payload - Base64 编码的 v1 AES-GCM 二进制载荷。
|
|
2722
|
-
* @param key - 加密时使用的非空 UTF-8
|
|
2720
|
+
* @param key - 加密时使用的非空 UTF-8 文本密钥
|
|
2723
2721
|
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串。
|
|
2724
2722
|
* @throws 载荷格式无效、密钥错误或认证失败时抛出错误。
|
|
2725
2723
|
*/
|
|
2726
2724
|
async function AESDecryptAuthenticated(payload, key) {
|
|
2727
|
-
if (key.trim().length === 0) throw new TypeError("
|
|
2725
|
+
if (key.trim().length === 0) throw new TypeError("The encryption key must not be empty.");
|
|
2728
2726
|
const decoded = decodeBase64Bytes(payload);
|
|
2729
|
-
if (decoded.length < 29 || decoded[0] !== authenticatedAesPayloadVersion) throw new TypeError("
|
|
2727
|
+
if (decoded.length < 29 || decoded[0] !== authenticatedAesPayloadVersion) throw new TypeError("Unsupported AES-GCM payload format or version.");
|
|
2730
2728
|
const nonce = decoded.subarray(1, 13);
|
|
2731
2729
|
const tag = decoded.subarray(13, 29);
|
|
2732
2730
|
const ciphertext = decoded.subarray(29);
|
|
2733
2731
|
const ciphertextAndTag = new Uint8Array(ciphertext.length + tag.length);
|
|
2734
2732
|
ciphertextAndTag.set(ciphertext);
|
|
2735
2733
|
ciphertextAndTag.set(tag, ciphertext.length);
|
|
2736
|
-
|
|
2734
|
+
assertWebCrypto("importKey", "decrypt");
|
|
2737
2735
|
const keyBytes = await SHA256Bytes(key);
|
|
2738
2736
|
const cryptoKey = await crypto.subtle.importKey("raw", toArrayBuffer(keyBytes), { name: "AES-GCM" }, false, ["decrypt"]);
|
|
2739
2737
|
const plaintext = await crypto.subtle.decrypt({
|
|
@@ -2741,7 +2739,7 @@ async function AESDecryptAuthenticated(payload, key) {
|
|
|
2741
2739
|
name: "AES-GCM",
|
|
2742
2740
|
tagLength: 128
|
|
2743
2741
|
}, cryptoKey, toArrayBuffer(ciphertextAndTag));
|
|
2744
|
-
return createDecodedText(
|
|
2742
|
+
return createDecodedText(decodeUtf8(plaintext));
|
|
2745
2743
|
}
|
|
2746
2744
|
/**
|
|
2747
2745
|
* 使用 PBKDF2-HMAC-SHA-256 派生密钥,再以 AES-256-GCM 认证加密 UTF-8 文本。
|
|
@@ -2749,18 +2747,18 @@ async function AESDecryptAuthenticated(payload, key) {
|
|
|
2749
2747
|
* @remarks 每次调用生成独立 16 字节盐与 12 字节 IV。输出是与 .NET `AESEncryptWithPassword`
|
|
2750
2748
|
* 一致的 v1 自描述载荷,不应由业务代码手动拆分或修改。密码加密不替代密钥管理。
|
|
2751
2749
|
* @param plaintext - 原始文本,不进行 JSON 推断;UTF-8 编码后最大 8 MiB。
|
|
2752
|
-
* @param password - 1 至 1024 UTF-8
|
|
2753
|
-
* @param iterations - PBKDF2 工作因子,默认 600,000
|
|
2750
|
+
* @param password - 1 至 1024 UTF-8 字节的秘密口令
|
|
2751
|
+
* @param iterations - PBKDF2 工作因子,默认 600,000
|
|
2754
2752
|
* @returns 认证密文字符串;相同输入每次产生不同结果。
|
|
2755
2753
|
* @throws 口令非法时抛出 `TypeError` 或 `RangeError`;明文过大时抛出 `RangeError`;
|
|
2756
|
-
* 运行时缺少 Web Crypto
|
|
2754
|
+
* 运行时缺少 Web Crypto 时抛出 `Error`。
|
|
2757
2755
|
*/
|
|
2758
2756
|
async function AESEncryptWithPassword(plaintext, password, iterations = defaultPbkdf2Iterations) {
|
|
2759
2757
|
const passwordBytes = encodeValidatedPassword(password);
|
|
2760
2758
|
const plaintextBytes = encodeUtf8(plaintext);
|
|
2761
|
-
if (plaintextBytes.length > maximumPlaintextBytes) throw new RangeError(`UTF-8
|
|
2759
|
+
if (plaintextBytes.length > maximumPlaintextBytes) throw new RangeError(`The UTF-8 plaintext must not exceed ${maximumPlaintextBytes} bytes.`);
|
|
2762
2760
|
const validatedIterations = validateIterations(iterations);
|
|
2763
|
-
|
|
2761
|
+
assertWebCrypto("importKey", "deriveKey", "encrypt");
|
|
2764
2762
|
const salt = GenerateRandomBytes(16);
|
|
2765
2763
|
const iv = GenerateRandomBytes(12);
|
|
2766
2764
|
const material = await crypto.subtle.importKey("raw", toArrayBuffer(passwordBytes), "PBKDF2", false, ["deriveKey"]);
|
|
@@ -2790,17 +2788,17 @@ async function AESEncryptWithPassword(plaintext, password, iterations = defaultP
|
|
|
2790
2788
|
/**
|
|
2791
2789
|
* 解密 {@link AESEncryptWithPassword} 生成的 v1 认证载荷。
|
|
2792
2790
|
*
|
|
2793
|
-
* @param payload - 未修改的 v1 载荷,最大约 16 MiB
|
|
2794
|
-
* @param password -
|
|
2791
|
+
* @param payload - 未修改的 v1 载荷,最大约 16 MiB 文本
|
|
2792
|
+
* @param password - 加密时使用的口令
|
|
2795
2793
|
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串。
|
|
2796
2794
|
* @throws 格式或字段非法时抛出 `TypeError`,载荷过大时抛出 `RangeError`,认证或密码
|
|
2797
2795
|
* 失败及缺少平台能力时抛出 `Error`。
|
|
2798
2796
|
*/
|
|
2799
2797
|
async function AESDecryptWithPassword(payload, password) {
|
|
2800
2798
|
const passwordBytes = encodeValidatedPassword(password);
|
|
2801
|
-
if (payload.length > maximumPayloadLength) throw new RangeError("
|
|
2799
|
+
if (payload.length > maximumPayloadLength) throw new RangeError("The encrypted payload exceeds the supported size.");
|
|
2802
2800
|
const parts = payload.split(":");
|
|
2803
|
-
if (parts.length !== 5 || parts[0] !== encryptedPayloadPrefix) throw new TypeError("
|
|
2801
|
+
if (parts.length !== 5 || parts[0] !== encryptedPayloadPrefix) throw new TypeError("Unsupported encrypted payload format.");
|
|
2804
2802
|
let iterations;
|
|
2805
2803
|
let salt;
|
|
2806
2804
|
let iv;
|
|
@@ -2810,12 +2808,11 @@ async function AESDecryptWithPassword(payload, password) {
|
|
|
2810
2808
|
salt = decodeBase64UrlBytes(parts[2] ?? "");
|
|
2811
2809
|
iv = decodeBase64UrlBytes(parts[3] ?? "");
|
|
2812
2810
|
ciphertext = decodeBase64UrlBytes(parts[4] ?? "");
|
|
2813
|
-
if (salt.length !== 16 || iv.length !== 12 || ciphertext.length < 16) throw new TypeError("
|
|
2811
|
+
if (salt.length !== 16 || iv.length !== 12 || ciphertext.length < 16) throw new TypeError("The encrypted payload contains invalid field lengths.");
|
|
2814
2812
|
} catch (cause) {
|
|
2815
|
-
throw new TypeError("
|
|
2813
|
+
throw new TypeError("The encrypted payload contains invalid fields.", { cause });
|
|
2816
2814
|
}
|
|
2817
|
-
|
|
2818
|
-
const textDecoder = getTextDecoder();
|
|
2815
|
+
assertWebCrypto("importKey", "deriveKey", "decrypt");
|
|
2819
2816
|
try {
|
|
2820
2817
|
const material = await crypto.subtle.importKey("raw", toArrayBuffer(passwordBytes), "PBKDF2", false, ["deriveKey"]);
|
|
2821
2818
|
const key = await crypto.subtle.deriveKey({
|
|
@@ -2833,9 +2830,9 @@ async function AESDecryptWithPassword(payload, password) {
|
|
|
2833
2830
|
name: "AES-GCM",
|
|
2834
2831
|
tagLength: 128
|
|
2835
2832
|
}, key, toArrayBuffer(ciphertext));
|
|
2836
|
-
return createDecodedText(
|
|
2833
|
+
return createDecodedText(decodeUtf8(plaintext));
|
|
2837
2834
|
} catch (cause) {
|
|
2838
|
-
throw new Error("
|
|
2835
|
+
throw new Error("Unable to authenticate or decrypt the payload.", { cause });
|
|
2839
2836
|
}
|
|
2840
2837
|
}
|
|
2841
2838
|
/**
|
|
@@ -2846,8 +2843,8 @@ async function AESDecryptWithPassword(payload, password) {
|
|
|
2846
2843
|
* @throws 模数小于 2,048 或不是 256 的倍数时抛出 `RangeError`。
|
|
2847
2844
|
*/
|
|
2848
2845
|
async function GenerateRSAKeyPair(modulusLength = 2048) {
|
|
2849
|
-
if (!Number.isSafeInteger(modulusLength) || modulusLength < 2048 || modulusLength % 256 !== 0) throw new RangeError("`modulusLength`
|
|
2850
|
-
|
|
2846
|
+
if (!Number.isSafeInteger(modulusLength) || modulusLength < 2048 || modulusLength % 256 !== 0) throw new RangeError("`modulusLength` must be a safe integer of at least 2048 divisible by 256.");
|
|
2847
|
+
assertWebCrypto("generateKey", "exportKey");
|
|
2851
2848
|
const keyPair = assertKeyPair(await crypto.subtle.generateKey({
|
|
2852
2849
|
hash: "SHA-256",
|
|
2853
2850
|
modulusLength,
|
|
@@ -2861,11 +2858,11 @@ async function GenerateRSAKeyPair(modulusLength = 2048) {
|
|
|
2861
2858
|
*
|
|
2862
2859
|
* @param plaintext - 要加密的 UTF-8 文本;长度必须满足 RSA-OAEP 模数限制。
|
|
2863
2860
|
* @param publicKeyPem - SubjectPublicKeyInfo PEM 公钥。
|
|
2864
|
-
* @returns Base64 编码的 RSA
|
|
2861
|
+
* @returns Base64 编码的 RSA 密文
|
|
2865
2862
|
* @throws 公钥格式无效或明文超过 RSA-OAEP 容量时抛出错误。
|
|
2866
2863
|
*/
|
|
2867
2864
|
async function RSAEncryptOAEP(plaintext, publicKeyPem) {
|
|
2868
|
-
|
|
2865
|
+
assertWebCrypto("importKey", "encrypt");
|
|
2869
2866
|
const key = await crypto.subtle.importKey("spki", fromPem(publicKeyPem, pemLabels.public), {
|
|
2870
2867
|
hash: "SHA-256",
|
|
2871
2868
|
name: "RSA-OAEP"
|
|
@@ -2876,30 +2873,30 @@ async function RSAEncryptOAEP(plaintext, publicKeyPem) {
|
|
|
2876
2873
|
/**
|
|
2877
2874
|
* 使用 RSA-OAEP/SHA-256 私钥解密 Base64 密文。
|
|
2878
2875
|
*
|
|
2879
|
-
* @param ciphertext - Base64 编码的 RSA
|
|
2880
|
-
* @param privateKeyPem - 未加密的 PKCS#8 PEM
|
|
2876
|
+
* @param ciphertext - Base64 编码的 RSA 密文
|
|
2877
|
+
* @param privateKeyPem - 未加密的 PKCS#8 PEM 私钥
|
|
2881
2878
|
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串。
|
|
2882
2879
|
* @throws 私钥、Base64 或密文无效时抛出错误。
|
|
2883
2880
|
*/
|
|
2884
2881
|
async function RSADecryptOAEP(ciphertext, privateKeyPem) {
|
|
2885
|
-
|
|
2882
|
+
assertWebCrypto("importKey", "decrypt");
|
|
2886
2883
|
const key = await crypto.subtle.importKey("pkcs8", fromPem(privateKeyPem, pemLabels.private), {
|
|
2887
2884
|
hash: "SHA-256",
|
|
2888
2885
|
name: "RSA-OAEP"
|
|
2889
2886
|
}, false, ["decrypt"]);
|
|
2890
2887
|
const plaintext = await crypto.subtle.decrypt({ name: "RSA-OAEP" }, key, toArrayBuffer(decodeBase64Bytes(ciphertext)));
|
|
2891
|
-
return createDecodedText(
|
|
2888
|
+
return createDecodedText(decodeUtf8(plaintext));
|
|
2892
2889
|
}
|
|
2893
2890
|
/**
|
|
2894
2891
|
* 使用 RSA-PSS/SHA-256 私钥签名文本或字节。
|
|
2895
2892
|
*
|
|
2896
|
-
* @param value - 要签名的 UTF-8
|
|
2897
|
-
* @param privateKeyPem - 未加密的 PKCS#8 PEM
|
|
2893
|
+
* @param value - 要签名的 UTF-8 文本或原始字节
|
|
2894
|
+
* @param privateKeyPem - 未加密的 PKCS#8 PEM 私钥
|
|
2898
2895
|
* @returns Base64 编码的 RSA-PSS 签名;盐长度固定为 32 字节。
|
|
2899
2896
|
* @throws 私钥格式无效或签名失败时抛出错误。
|
|
2900
2897
|
*/
|
|
2901
2898
|
async function RSASignPSS(value, privateKeyPem) {
|
|
2902
|
-
|
|
2899
|
+
assertWebCrypto("importKey", "sign");
|
|
2903
2900
|
const key = await crypto.subtle.importKey("pkcs8", fromPem(privateKeyPem, pemLabels.private), {
|
|
2904
2901
|
hash: "SHA-256",
|
|
2905
2902
|
name: "RSA-PSS"
|
|
@@ -2913,14 +2910,14 @@ async function RSASignPSS(value, privateKeyPem) {
|
|
|
2913
2910
|
/**
|
|
2914
2911
|
* 使用 RSA-PSS/SHA-256 公钥验证 Base64 签名。
|
|
2915
2912
|
*
|
|
2916
|
-
* @param value - 签名时使用的 UTF-8
|
|
2917
|
-
* @param signature - Base64 编码的 RSA-PSS
|
|
2913
|
+
* @param value - 签名时使用的 UTF-8 文本或原始字节
|
|
2914
|
+
* @param signature - Base64 编码的 RSA-PSS 签名
|
|
2918
2915
|
* @param publicKeyPem - SubjectPublicKeyInfo PEM 公钥。
|
|
2919
2916
|
* @returns 签名与内容、公钥匹配时返回 `true`。
|
|
2920
2917
|
* @throws 公钥或 Base64 格式无效时抛出错误。
|
|
2921
2918
|
*/
|
|
2922
2919
|
async function RSAVerifyPSS(value, signature, publicKeyPem) {
|
|
2923
|
-
|
|
2920
|
+
assertWebCrypto("importKey", "verify");
|
|
2924
2921
|
const key = await crypto.subtle.importKey("spki", fromPem(publicKeyPem, pemLabels.public), {
|
|
2925
2922
|
hash: "SHA-256",
|
|
2926
2923
|
name: "RSA-PSS"
|
|
@@ -2937,7 +2934,7 @@ async function RSAVerifyPSS(value, signature, publicKeyPem) {
|
|
|
2937
2934
|
* @returns 未加密 PKCS#8 私钥和 SubjectPublicKeyInfo 公钥组成的 PEM 密钥对。
|
|
2938
2935
|
*/
|
|
2939
2936
|
async function GenerateECDSAKeyPair(namedCurve = "P-256") {
|
|
2940
|
-
|
|
2937
|
+
assertWebCrypto("generateKey", "exportKey");
|
|
2941
2938
|
const keyPair = assertKeyPair(await crypto.subtle.generateKey({
|
|
2942
2939
|
name: "ECDSA",
|
|
2943
2940
|
namedCurve
|
|
@@ -2948,13 +2945,13 @@ async function GenerateECDSAKeyPair(namedCurve = "P-256") {
|
|
|
2948
2945
|
* 使用 ECDSA 私钥签名文本或字节。
|
|
2949
2946
|
*
|
|
2950
2947
|
* @remarks Web Crypto 返回 IEEE P1363 固定字段拼接格式,与 .NET 实现一致。
|
|
2951
|
-
* @param value - 要签名的 UTF-8
|
|
2952
|
-
* @param privateKeyPem - 未加密的 EC PKCS#8 PEM
|
|
2953
|
-
* @param namedCurve - 私钥使用的 NIST
|
|
2948
|
+
* @param value - 要签名的 UTF-8 文本或原始字节
|
|
2949
|
+
* @param privateKeyPem - 未加密的 EC PKCS#8 PEM 私钥
|
|
2950
|
+
* @param namedCurve - 私钥使用的 NIST 曲线
|
|
2954
2951
|
* @returns Base64 编码的 IEEE P1363 ECDSA 签名。
|
|
2955
2952
|
*/
|
|
2956
2953
|
async function ECDSASign(value, privateKeyPem, namedCurve = "P-256") {
|
|
2957
|
-
|
|
2954
|
+
assertWebCrypto("importKey", "sign");
|
|
2958
2955
|
const key = await crypto.subtle.importKey("pkcs8", fromPem(privateKeyPem, pemLabels.private), {
|
|
2959
2956
|
name: "ECDSA",
|
|
2960
2957
|
namedCurve
|
|
@@ -2969,14 +2966,14 @@ async function ECDSASign(value, privateKeyPem, namedCurve = "P-256") {
|
|
|
2969
2966
|
/**
|
|
2970
2967
|
* 使用 ECDSA 公钥验证 Base64 签名。
|
|
2971
2968
|
*
|
|
2972
|
-
* @param value - 签名时使用的 UTF-8
|
|
2969
|
+
* @param value - 签名时使用的 UTF-8 文本或原始字节
|
|
2973
2970
|
* @param signature - Base64 编码的 IEEE P1363 ECDSA 签名。
|
|
2974
2971
|
* @param publicKeyPem - EC SubjectPublicKeyInfo PEM 公钥。
|
|
2975
|
-
* @param namedCurve - 公钥使用的 NIST
|
|
2972
|
+
* @param namedCurve - 公钥使用的 NIST 曲线
|
|
2976
2973
|
* @returns 签名与内容、公钥和曲线匹配时返回 `true`。
|
|
2977
2974
|
*/
|
|
2978
2975
|
async function ECDSAVerify(value, signature, publicKeyPem, namedCurve = "P-256") {
|
|
2979
|
-
|
|
2976
|
+
assertWebCrypto("importKey", "verify");
|
|
2980
2977
|
const key = await crypto.subtle.importKey("spki", fromPem(publicKeyPem, pemLabels.public), {
|
|
2981
2978
|
name: "ECDSA",
|
|
2982
2979
|
namedCurve
|
|
@@ -2994,7 +2991,7 @@ async function ECDSAVerify(value, signature, publicKeyPem, namedCurve = "P-256")
|
|
|
2994
2991
|
* @returns 未加密 PKCS#8 私钥和 SubjectPublicKeyInfo 公钥组成的 PEM 密钥对。
|
|
2995
2992
|
*/
|
|
2996
2993
|
async function GenerateECDHKeyPair(namedCurve = "P-256") {
|
|
2997
|
-
|
|
2994
|
+
assertWebCrypto("generateKey", "exportKey");
|
|
2998
2995
|
const keyPair = assertKeyPair(await crypto.subtle.generateKey({
|
|
2999
2996
|
name: "ECDH",
|
|
3000
2997
|
namedCurve
|
|
@@ -3005,13 +3002,13 @@ async function GenerateECDHKeyPair(namedCurve = "P-256") {
|
|
|
3005
3002
|
* 使用本方 ECDH 私钥与对方 ECDH 公钥派生共享秘密。
|
|
3006
3003
|
*
|
|
3007
3004
|
* @remarks 返回值仍需经过合适的 KDF 后才能作为对称密钥,不应直接长期存储。
|
|
3008
|
-
* @param privateKeyPem - 本方未加密的 EC PKCS#8 PEM
|
|
3005
|
+
* @param privateKeyPem - 本方未加密的 EC PKCS#8 PEM 私钥
|
|
3009
3006
|
* @param publicKeyPem - 对方的 EC SubjectPublicKeyInfo PEM 公钥。
|
|
3010
|
-
* @param namedCurve - 双方密钥使用的 NIST
|
|
3011
|
-
* @returns 曲线字段长度的原始 ECDH
|
|
3007
|
+
* @param namedCurve - 双方密钥使用的 NIST 曲线
|
|
3008
|
+
* @returns 曲线字段长度的原始 ECDH 共享秘密
|
|
3012
3009
|
*/
|
|
3013
3010
|
async function DeriveECDHSecret(privateKeyPem, publicKeyPem, namedCurve = "P-256") {
|
|
3014
|
-
|
|
3011
|
+
assertWebCrypto("importKey", "deriveBits");
|
|
3015
3012
|
const [privateKey, publicKey] = await Promise.all([crypto.subtle.importKey("pkcs8", fromPem(privateKeyPem, pemLabels.private), {
|
|
3016
3013
|
name: "ECDH",
|
|
3017
3014
|
namedCurve
|
|
@@ -3030,14 +3027,15 @@ async function DeriveECDHSecret(privateKeyPem, publicKeyPem, namedCurve = "P-256
|
|
|
3030
3027
|
* 使用 ECDH 后以 SHA-256 派生共享密钥。
|
|
3031
3028
|
*
|
|
3032
3029
|
* @remarks 相比直接使用原始共享秘密,此入口与 .NET `DeriveECDHKeySHA256` 一致并固定输出 32 字节。
|
|
3033
|
-
* @param privateKeyPem - 本方未加密的 EC PKCS#8 PEM
|
|
3030
|
+
* @param privateKeyPem - 本方未加密的 EC PKCS#8 PEM 私钥
|
|
3034
3031
|
* @param publicKeyPem - 对方的 EC SubjectPublicKeyInfo PEM 公钥。
|
|
3035
|
-
* @param namedCurve - 双方密钥使用的 NIST
|
|
3036
|
-
* @returns 32
|
|
3032
|
+
* @param namedCurve - 双方密钥使用的 NIST 曲线
|
|
3033
|
+
* @returns 32 字节共享密钥
|
|
3037
3034
|
*/
|
|
3038
3035
|
async function DeriveECDHKeySHA256(privateKeyPem, publicKeyPem, namedCurve = "P-256") {
|
|
3039
3036
|
const secret = await DeriveECDHSecret(privateKeyPem, publicKeyPem, namedCurve);
|
|
3040
|
-
|
|
3037
|
+
assertWebCrypto("digest");
|
|
3038
|
+
const digest = await crypto.subtle.digest("SHA-256", toArrayBuffer(secret));
|
|
3041
3039
|
return new Uint8Array(digest);
|
|
3042
3040
|
}
|
|
3043
3041
|
//#endregion
|