# @effected/crypto 中文文档

规范地址：https://npmcn.com/package/%40effected/crypto/

版本：0\.7\.0
npm 元数据更新时间：2026-11-10T15:05:24.855Z
中文译文更新时间：2026-11-13T09:21:45.642Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40effected/crypto
原文对照：https://npmcn.com/package/%40effected/crypto/?lang=original
项目主页：https://github.com/spencerbeggs/effected/tree/main/packages/crypto#readme
源码仓库：https://github.com/spencerbeggs/effected

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：108 kB，20 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=24\.11\.0
- 运行依赖：0 个
- peer 依赖：effect ~4\.1\.0
- 首次发布：2026-10-23T03:50:59.452Z
- 最新版本发布：2026-11-10T15:05:24.131Z
- 近 30 天下载量：6324（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：10（近 12 个月 10 个）

## 中文简介

HMAC,消息摘要,常时比较和AWS SigV4请求在Effect v4的核心Mac和摘要服务上签名,为工人安全.

## 安装

```sh
npm install @effected/crypto
```

也可以使用：`pnpm add @effected/crypto`、`yarn add @effected/crypto`、`bun add @effected/crypto`

## README

# @ 受影响/ crypto

[![npm](https://img.shields.io/npm/v/@effected%2Fcrypto?label=npm&color=cb3837)](https://www.npmjs.com/package/@effected/crypto) [![License: MIT](https://img.shields.io/badge/License-MIT-4caf50.svg)](https://opensource.org/licenses/MIT) [![Node.js %3E%3D24.11.0](https://img.shields.io/badge/Node.js-%3E%3D24.11.0-5fa04e.svg)](https://nodejs.org/)

HMAC 签名和验证、消息摘要、恒时比较和 AWS 签名版本 4 请求签名 Effect v4, 以核心为基础 `effect/crypto`是因为 `Mac` 财务报告和审定财务报表 `Digest` 服务。 没有 `node:` 导入,所以同样的代码运行在节点,邦,德诺,浏览器和Cloudflare Workers上. 还有船 `Fingerprint`,稳定的SHA-256指纹的JSON形状值,有三向漂移的决定 `HttpRedaction`,它从 HTTP 错误中剥离证书,然后保存它们.

> **预`1.0.0`。 。 。 .** 此软件包是 `@effected/*` 工具箱,在 稳定效果 v4并仍在 `0.x` 发展。 为了保持你的 `effect` 财务报告和审定财务报表 `@effect/*` 该工具包在线上构建和测试、安装 [`@effected/pnpm-plugin-effected`](https://www.npmjs.com/package/@effected/pnpm-plugin-effected)。 。 。 .
> 
> **稳定:不稳定.** 此软件包的 API 表面尚未考虑 完成并可能改变 `0.x` 释放 准确的版本。 满 政策: [释放策略](https://github.com/spencerbeggs/effected#release-strategy)。 。 。 .

## 为什么

核心 `effect/crypto` 已计算和检查MAC(`Mac`),散列字节(`Digest`)并在常时比较字节(`Mac.timingSafeEqual`) (中文(简体) ). 已经持有时直接使用 `Mac` 密钥和信件为一体 `Uint8Array`,并想要原始标记字节返回.

当您需要核心留给您时,请联系这个包:

-   **网络呼和API使用的形状.** `Hmac` 使用一个 `Redacted` 字符串或生字节作为密钥,而字符串、字节或部件列表作为消息,所以 `${timestamp}.` 加上一个活体的签名,没有自己建造合并的缓冲器。 它返回或证实, `hex`, (中文(简体) ), `base64` 或者说 `base64url` 还有字节 无法解码的签名 `false` 而不是失败.
-   **抓着前缀加一具尸体.** `Hash.digest` 接收相同的信件并返回与 `Hmac.sign`,超过核心的 `Digest`这样 `sha256(window + "." + body)` 是一个电话,并匆忙地 到底什么 `node:crypto`是因为 `createHash().update(a).update(b)` 胡说八道.
-   **比较一个共同的秘密.** `ConstantTime.secretsEqual` MAC在进行对比之前,会在新的随机键下将两侧相接,因此密钥的长度和相匹配的前缀都没有漏出.
-   **AWS SigV4 (英语).** `SigV4.sign` 签名 S3 或任何 S3 兼容商店( R2, Minio, Spaces) 的请求, 并返回它所签名的精确编码路径和查询 .
-   **漂流探测.** `Fingerprint.of` 拥有一种具有价值的能动JSON形式 `Fingerprint.decide` 3个指纹变成 `Apply`, (中文(简体) ), `Unchanged` 或者说 `Drift`。 。 。 .
-   **安全保存 HTTP 错误 .** 科雷尔 `HttpClientError` 保持现场请求, `authorization` 包括一个可读字段. `HttpRedaction.cause` 返回替换的证书副本.

每一次失败都是核心的 `CryptoError` 或此软件包中的一个被标记的输入出错; 里面没有读取时钟 .

## 安装

```bash
npm install @effected/crypto effect
```

```bash
pnpm add @effected/crypto effect
```

在节点上,它需要Node.js QQ24.11.0;它还运行在任何其他WebCrypto运行时间上,Cloudflare Workers也包括在内. `effect` v4是唯一的对等方,没有运行时间依赖性. 全体 `@effected/*` 软件包仅限无害环境管理.

## 提供什么

关键业务和散列业务需要核心服务 `R`在程序边缘提供一次:

| 运行 | 要求数 |
| --- | --- |
| `Hmac.sign`, (中文(简体) ), `Hmac.verify`, (中文(简体) ), `ConstantTime.secretsEqual`, (中文(简体) ), `SigV4.signingKey` | `Mac.Mac` |
| `SigV4.sign` | `Mac.Mac` 财务报告和审定财务报表 `Digest.Digest` |
| `Hash.digest`, (中文(简体) ), `SigV4.canonicalize`, (中文(简体) ), `Fingerprint.of` | `Digest.Digest` |

`ConstantTime.equal`, (中文(简体) ), `SigV4.encodePath`, (中文(简体) ), `Fingerprint.decide`, (中文(简体) ), `Fingerprint.canonicalizeResult` 和每个 `HttpRedaction` 方法很纯洁,不需要任何东西.

| 服务 | 任何 WebCrypto 运行时间( 包括工人) | 节点 |
| --- | --- | --- |
| `Mac.Mac` | `Mac.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto))` 从 `effect/crypto` | `NodeCrypto.layerMac` 从 `@effect/platform-node` |
| `Digest.Digest` | `Digest.layerWebCrypto` 从 `effect/crypto` | `NodeCrypto.layerDigest` 从 `@effect/platform-node` |

```ts
import { Layer } from "effect";
import { Digest, Mac, SecureRandom } from "effect/crypto";

const CryptoLive = Layer.mergeAll(
  Digest.layerWebCrypto,
  Mac.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto)),
);
```

在节点上, `Layer.mergeAll(NodeCrypto.layerMac, NodeCrypto.layerDigest)` 运行 `node:crypto` 换句话说. `NodeServices.layer` 提供两种服务。 在WebCrypto地层上,每个HMAC都等待着真正的承诺,所以测试叉出一个然后调整 `TestClock` 可以吊起:等待它,或者把钟锁上.

## 签名和验证 HMAC

```ts
import { Hmac } from "@effected/crypto";
import type { Redacted } from "effect";

const signDelivery = (secret: Redacted.Redacted<string>, timestamp: string, body: Uint8Array) =>
  Hmac.sign({ algorithm: "SHA-256", key: secret, message: [`${timestamp}.`, body], encoding: "hex" });
// Effect<string, CryptoError, Mac>

const verifyDelivery = (secret: Redacted.Redacted<string>, timestamp: string, body: Uint8Array, signature: string) =>
  Hmac.verify({ algorithm: "SHA-256", key: secret, message: [`${timestamp}.`, body], signature, encoding: "hex" });
// Effect<boolean, CryptoError, Mac>
```

`algorithm` 实值 `SHA-256` 或者说 `SHA-512`。 。 。 . `encoding` 实值 `hex` (产出较低,投入较低), `base64` (插入), `base64url` (未添加)或,当省略 `sign`,生字节. A 类 `Redacted` 键被用作它的UTF-8字节并只在导入时读取. `verify` 解码收到的签名, 用核心的常时检查 `Mac.verify`,因此,任何解码到右MAC验证的拼写;一个不解码或者长度错误的拼写,回答 `false`。空密钥失败 `CryptoError`。 。 。 .

## 一条消息

```ts
import { Hash } from "@effected/crypto";

const archiveKey = (window: number, body: Uint8Array) =>
  Hash.digest({ algorithm: "SHA-256", message: [`${window}.`, body], encoding: "hex" });
// Effect<string, never, Digest>
```

`algorithm` 实值 `MD5`, (中文(简体) ), `SHA-1`, (中文(简体) ), `SHA-256`, (中文(简体) ), `SHA-384` 或者说 `SHA-512`每个算法核心 `Digest` 支持; MD5和SHA-1因相撞阻力被打破,所以只使用它们来匹配现有的协议或校验和. 消息和编码是那些 `Hmac.sign`:字符串是它的 UTF-8 字节(一个孤独的代词变为 `EF BF BD`,与 `node:crypto` ),列表是其部分'字节按顺序加入,并且 `encoding` 实值 `hex`, (中文(简体) ), `base64`, (中文(简体) ), `base64url` 或者,在省略时,生字节. 每个部分都是自己编码的, 所以一个替身对子 分成两个部分是两个孤独的替人,因为它跨越两个 `update` 打电话 文摘不能失败。 打开 `Digest.layerWebCrypto` 无运行时间 `crypto.subtle` 是一个缺陷: SHA 文摘会死, 而 MD5 运行在纯 TypeScript 中并持续工作.

## 恒定时间比较

```ts
import { ConstantTime } from "@effected/crypto";
import type { Redacted } from "effect";

// Two digests or MACs, whose length is public:
const sameDigest = ConstantTime.equal(expectedHex, receivedHex);
// boolean

// A shared secret, whose length is not:
const checkApiKey = (expected: Redacted.Redacted<string>, received: string) =>
  ConstantTime.secretsEqual(expected, received);
// Effect<boolean, CryptoError, Mac>
```

`equal` 取出两个字节数组或两个字符串( 与 UTF-8 相比)并返回 `false` 长度不匹配,对消化无害. `secretsEqual` 在每个呼叫中生成一款新鲜的HMAC-SHA256密钥,MAC将预期的一面并验证接收的一面对它,因此只有两个32字节的标签被比对过. 两者都是最优的:一个JavaScript引擎可以自由优化一个回路,因此JavaScript中的任何比较都不能保证恒定时间.

## 签署 S3 请求

```ts
import type { SigV4Credentials } from "@effected/crypto";
import { SigV4 } from "@effected/crypto";
import { Effect } from "effect";

const readFirstBytes = (credentials: SigV4Credentials, host: string, key: string, now: Date) =>
  SigV4.sign(
    {
      method: "GET",
      host,
      path: key,
      headers: { range: "bytes=0-9" },
      payloadHash: SigV4.EMPTY_PAYLOAD_HASH,
      now,
    },
    credentials,
  ).pipe(
    Effect.map((signed) => ({
      url: `https://${host}${signed.path}${signed.query === "" ? "" : `?${signed.query}`}`,
      headers: signed.headers,
    })),
  );
// Effect<{ url: string; headers: Record<string, string> }, SigV4PathError | SigV4EncodingError | CryptoError, Digest | Mac>
```

`SigV4Credentials` 持有 `accessKeyId`页:1 `Redacted` `secretAccessKey`,可选 `Redacted` `sessionToken` (同意为 `x-amz-security-token`, (中文(简体) ), `region` (单位:千美元)`auto` 用于R2和 `service`。该请求自签即刻进入 `now`作为 `Date` 或一个 `DateTime`,因此可以复制签名。 过 `path` 财务报告和审定财务报表 `query` 未编码 : `sign` 一次编码一个路段( 取消) `!'()*`,哪个 `encodeURIComponent` ),排序和编码查询,并返回其签名的字符串 `path` 财务报告和审定财务报表 `query`。发送精确的这些;重新编码它们打破了签名.

载荷散列是SHA-256 `body` 除非你通过 `payloadHash`数字 : `SigV4.EMPTY_PAYLOAD_HASH` 给一个空的躯体, `SigV4.UNSIGNED_PAYLOAD` 用来上传一条流经,或计算出一条散列。 回来的 `headers` 是你们的,名字小写,加 `host`, (中文(简体) ), `x-amz-date`, (中文(简体) ), `x-amz-content-sha256`,当有一个会话符号 `authorization`。 。 。 .

一条有段的路径 : `.` 或者说 `..` 失败 `SigV4PathError` 在签名之前 : 每个 URL 解析器都会崩溃这样的一段, 因此请求会到达与签名的路径不同的路径 。 方法、 主机、 路径、 查询密钥或值, 或信头名称或值 `SigV4EncodingError`,命名组件,而不是死在里面 `encodeURIComponent`。 。 。 . `SigV4.canonicalize` 财务报告和审定财务报表 `SigV4.signingKey` 用于针对 AWS 所记录的示例进行定点测试.

## 探测漂移

```ts
import { Fingerprint } from "@effected/crypto";
import { Effect } from "effect";

const plan = (applied: string | undefined, live: unknown, desired: unknown) =>
  Effect.gen(function* () {
    const decision = Fingerprint.decide({
      applied,
      live: yield* Fingerprint.of(live),
      desired: yield* Fingerprint.of(desired),
    });
    switch (decision._tag) {
      case "Apply":
        return "apply";
      case "Unchanged":
        return "skip";
      case "Drift":
        return `changed outside this tool: ${decision.applied} is now ${decision.live}`;
    }
  });
// Effect<string, FingerprintError, Digest>
```

一个指纹是犬科JSON文字的下例SHA-256六:按每个深度排序的对象密钥,阵列顺序,以及 `undefined` 成员放弃,所以一个没有钥匙和一个 `undefined` 指纹一样 不然就这样 `JSON.stringify`,带有 `Date` 写为 ISO 字符串。 没有忠实的JSON表格的价值被拒绝 `FingerprintError`,从不强迫:a `bigint`,函数,符号,非无限数,周期,或任何非一个对象 `Date`、数组或平面对象。 包括一个 `Map`和效果数据类型,例如 `Option`随便 `toJSON` 它们携带;首先将其编码为普通的JSON,例如: `Schema`。 。 。 . `Fingerprint.canonicalizeResult` 返回条形文字为 `Result`解释一份漂移报告.

`decide` 运用其规则。 没有 `applied` 基线答案 `Apply`A级 `live` 区别于 `applied` 实值 `Drift`,并带有两者,即使 `desired` 也变了,因为那是你最想在事情被掩盖之前被告知的时候。 否则,a `desired` 区别于 `applied` 实值 `Apply`,其他东西是 `Unchanged`后 `Apply`将想要的指纹作为下一轮的指纹 `applied`这三种情况是: `DriftApply`, (中文(简体) ), `DriftUnchanged` 财务报告和审定财务报表 `DriftDetected`,以及 `DriftDecision` 是他们的计划结合, 所以一个决定可以被记录或存储 并再次解码.

## 保留 HTTP 错误而无其权限

```ts
import { HttpRedaction } from "@effected/crypto";
import { Effect, Redacted, Schema } from "effect";
import { HttpClient } from "effect/http";

class RegistryError extends Schema.TaggedError<RegistryError>()("RegistryError", {
  cause: Schema.Defect(),
}) {}

const fetchManifest = (token: Redacted.Redacted<string>, url: string) =>
  HttpClient.get(url, { headers: { authorization: `Bearer ${Redacted.value(token)}` } }).pipe(
    Effect.mapError((cause) => new RegistryError({ cause: HttpRedaction.cause(cause) })),
  );

HttpRedaction.url("https://user:pw@registry.test/pkg?token=abc&page=2");
// "https://REDACTED:REDACTED@registry.test/pkg?token=REDACTED&page=2"
```

核心仅在出现错误时才修改证书( Q) :`toJSON`,检查). 一个记录器,序列器 或任何其它东西 移动物体仍然读取它们, 所以擦除 在您保持的原因. `HttpRedaction.cause` 副本 a `HttpClientError`,一个赤裸裸的错误原因,一个请求或一个回应,并传递其他一切没有改变.

副本保存方法、状态和 URL, 用户info 和敏感查询值被替换 `REDACTED`: 核心自己的列表, SigV4 预先签名的集, Azure SAS `sig`, (中文(简体) ), `token`, (中文(简体) ), `*_token`, (中文(简体) ), `api_key`, (中文(简体) ), `client_secret`, (中文(简体) ), `secret`, (中文(简体) ), `password` 财务报告和审定财务报表 `code`。敏感页眉值(`authorization`, (中文(简体) ), `proxy-authorization`, (中文(简体) ), `cookie`, (中文(简体) ), `x-api-key`, (中文(简体) ), `private-token`,则 `x-*-token`, (中文(简体) ), `x-*-api-key` 财务报告和审定财务报表 `x-*-signature` 家庭)改为: `REDACTED`, (中文(简体) ), `set-cookie` 财务报告和审定财务报表 `cookie` 从响应标题中掉出,尸体被丢出。 窝在外的事业,例如 `TypeError` 失败的获取拒绝, 重建在同一原型上, 并带有信件和堆栈擦拭. `HttpRedaction.text` 清除 URL 和 `Bearer`, (中文(简体) ), `Basic` 财务报告和审定财务报表 `token` 以线性时间从自由文本中取出证书,以及 `HttpRedaction.error`, (中文(简体) ), `request`, (中文(简体) ), `response` 财务报告和审定财务报表 `isSensitiveHeader` 是单个块.

## 错误

| 错误 | 升为 | 含义 |
| --- | --- | --- |
| `CryptoError` (核心, `effect/crypto`),原因 `InvalidKey` | `Hmac.sign`, (中文(简体) ), `Hmac.verify` | 密钥为空. |
| `CryptoError` (核心),任何原因 | `Hmac`, (中文(简体) ), `ConstantTime.secretsEqual`, (中文(简体) ), `SigV4.sign`, (中文(简体) ), `SigV4.signingKey` | 这个 `Mac` 层拒绝或处理不当它应该接受的密钥。 行为良好的层无法从 `secretsEqual` 或SigV4,其键从不为空. |
| `SigV4PathError` | `SigV4.sign`, (中文(简体) ), `SigV4.canonicalize` | 路径有一个部分 准确的 `.` 或者说 `..`. 承运人 `path` 财务报告和审定财务报表 `segment`。 。 。 . |
| `SigV4EncodingError` | `SigV4.sign`, (中文(简体) ), `SigV4.canonicalize` | 一个方法,主机,路径,查询密钥或值,或标题名称或值持有一个孤独的UTF-16代理;一个字符串 `body` 未选中。 卡莉丝 `component` (单位:千美元)`method`, (中文(简体) ), `host`, (中文(简体) ), `path`, (中文(简体) ), `query-key`, (中文(简体) ), `query-value`, (中文(简体) ), `header-name`, (中文(简体) ), `header-value`并且,当有一个 `subject` (密钥或头名). |
| `FingerprintError` | `Fingerprint.of`, (中文(简体) ), `Fingerprint.canonicalizeResult` | 该值没有Canonical JSON形式. 卡莉丝 `path` (类似JSON的指针位置, `""` 为根)和 `detail` (在那里发现了什么). |

`CryptoError` 说明电线是错的 所以把它变成一个缺陷 `Effect.orDie`。标记错误是调用器输入:用 `Effect.catchTag("SigV4PathError", ...)`, (中文(简体) ), `Effect.catchTag("SigV4EncodingError", ...)` 或者说 `Effect.catchTag("FingerprintError", ...)`此套件不再出口 `CryptoError`; 从 `effect/crypto`。在 WebCrypto 层上,运行时间没有 `crypto.subtle` 根本不是失败:效果会死亡. `Hmac.verify` 报告错误的签名为 `false`,绝不是错误.

## 测试

包件没有测试双倍, 因为不需要: 提供 WebCrypto 从 [提供什么](https://npmcn.com/package/%40effected/crypto/#readme-what-to-provide) 在测试中,与工人一样。 对照独立实施来检查自己的签名,与 `node:crypto`是因为 `createHmac` 在测试中,或提供 `NodeCrypto.layerMac` 财务报告和审定财务报表 `NodeCrypto.layerDigest` 并对两个家庭进行同样的测试。 每一次手术都要从呼叫者那里抽出时间 所以不需要测试 `TestClock` 签名或指纹.

## 它不会做的

-   **长一溪.** `Hash` 财务报告和审定财务报表 `Digest` hash 字节已经存在于内存中,没有WebCrypto 原始的干流,所以将一个文件不按内存需要来抓 `node:crypto`。 。 。 .
-   **把盒子封起来.** GitHub的秘密 API需要的封存盒子 [`@effected/github`](https://www.npmjs.com/package/@effected/github)因为它的引擎会增加运行时间的依赖性.
-   **RFC8785的指纹.** `Fingerprint` 滴数 `undefined` 成员,RFC 8785拒绝;对于RFC 8785 canonic JSON,使用 `JsoncFingerprint` 从 [`@effected/jsonc`](https://www.npmjs.com/package/@effected/jsonc)。 。 。 .
-   **校验网友.** 供应商计划(GitHub、Frede、标准Webhooks、Sanity) [`@effected/webhooks`](https://www.npmjs.com/package/@effected/webhooks),它基于此软件包.

## 许可证

[麻省理工学院](https://npmcn.com/package/%40effected/crypto/LICENSE)

## 常见问题

### @effected/crypto 是什么？

HMAC,消息摘要,常时比较和AWS SigV4请求在Effect v4的核心Mac和摘要服务上签名,为工人安全\.

### 如何安装 @effected/crypto？

在项目目录运行 npm install @effected/crypto，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：effect（~4\.1\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @effected/crypto 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @effected/crypto 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @effected/crypto 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=24\.11\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @effected/crypto 可以商用吗？

@effected/crypto 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @effected/crypto 的下载量有多少？

根据 npm 官方下载统计，@effected/crypto 在 2026/10/13 – 2026/11/11 这 30 天内被下载 6,324 次，日均约 211 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @effected/crypto 还在维护吗？

最新版本 0\.7\.0 发布于 2026/11/10（2 天前）；至今共发布 10 个版本，其中过去 12 个月发布了 10 个；首次发布于 2026/10/23。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

