@effected/crypto

中文文档
0.7.0发布于 2026/11/10MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 6324

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

中文使用文档
文档目录13 节
  1. 为什么
  2. 安装
  3. 提供什么
  4. 签名和验证 HMAC
  5. 一条消息
  6. 恒定时间比较
  7. 签署 S3 请求
  8. 探测漂移
  9. 保留 HTTP 错误而无其权限
  10. 错误
  11. 测试
  12. 它不会做的
  13. 许可证

@ 受影响/ crypto

npm License: MIT Node.js %3E%3D24.11.0

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。 。 。 .

稳定:不稳定. 此软件包的 API 表面尚未考虑 完成并可能改变 0.x 释放 准确的版本。 满 政策: 释放策略。 。 。 .

为什么

核心 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 或此软件包中的一个被标记的输入出错; 里面没有读取时钟 .

安装

npm install @effected/crypto effect
pnpm add @effected/crypto effect

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

提供什么

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

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

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

服务任何 WebCrypto 运行时间( 包括工人)节点
Mac.MacMac.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto)) 从 effect/cryptoNodeCrypto.layerMac 从 @effect/platform-node
Digest.DigestDigest.layerWebCrypto 从 effect/cryptoNodeCrypto.layerDigest 从 @effect/platform-node
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

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。 。 。 .

一条消息

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 中并持续工作.

恒定时间比较

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 请求

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 所记录的示例进行定点测试.

探测漂移

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

保留 HTTP 错误而无其权限

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:[email protected]/pkg?token=abc&page=2");
// "https://REDACTED:[email protected]/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),原因 InvalidKeyHmac.sign, (中文(简体) ), Hmac.verify密钥为空.
CryptoError (核心),任何原因Hmac, (中文(简体) ), ConstantTime.secretsEqual, (中文(简体) ), SigV4.sign, (中文(简体) ), SigV4.signingKey这个 Mac 层拒绝或处理不当它应该接受的密钥。 行为良好的层无法从 secretsEqual 或SigV4,其键从不为空.
SigV4PathErrorSigV4.sign, (中文(简体) ), SigV4.canonicalize路径有一个部分 准确的 . 或者说 ... 承运人 path 财务报告和审定财务报表 segment。 。 。 .
SigV4EncodingErrorSigV4.sign, (中文(简体) ), SigV4.canonicalize一个方法,主机,路径,查询密钥或值,或标题名称或值持有一个孤独的UTF-16代理;一个字符串 body 未选中。 卡莉丝 component (单位:千美元)method, (中文(简体) ), host, (中文(简体) ), path, (中文(简体) ), query-key, (中文(简体) ), query-value, (中文(简体) ), header-name, (中文(简体) ), header-value并且,当有一个 subject (密钥或头名).
FingerprintErrorFingerprint.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 从 提供什么 在测试中,与工人一样。 对照独立实施来检查自己的签名,与 node:crypto是因为 createHmac 在测试中,或提供 NodeCrypto.layerMac 财务报告和审定财务报表 NodeCrypto.layerDigest 并对两个家庭进行同样的测试。 每一次手术都要从呼叫者那里抽出时间 所以不需要测试 TestClock 签名或指纹.

它不会做的

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

许可证

麻省理工学院

版本与发布记录

@effected/crypto 共发布 10 个版本,首次发布于 2026/10/23;过去 12 个月发布了 10 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.7.0latest2026/11/102 天前
0.6.02026/11/094 天前
0.5.02026/11/0310 天前
0.4.02026/11/0112 天前
0.3.02026/10/2915 天前
0.2.12026/10/2618 天前

关于 @effected/crypto 的常见问题

@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 和提交记录判断。