@ 受影响/ crypto
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.secretsEqualMAC在进行对比之前,会在新的随机键下将两侧相接,因此密钥的长度和相匹配的前缀都没有漏出. - AWS SigV4 (英语).
SigV4.sign签名 S3 或任何 S3 兼容商店( R2, Minio, Spaces) 的请求, 并返回它所签名的精确编码路径和查询 . - 漂流探测.
Fingerprint.of拥有一种具有价值的能动JSON形式Fingerprint.decide3个指纹变成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.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 |
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),原因 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 从 提供什么 在测试中,与工人一样。 对照独立实施来检查自己的签名,与 node:crypto是因为 createHmac 在测试中,或提供 NodeCrypto.layerMac 财务报告和审定财务报表 NodeCrypto.layerDigest 并对两个家庭进行同样的测试。 每一次手术都要从呼叫者那里抽出时间 所以不需要测试 TestClock 签名或指纹.
它不会做的
- 长一溪.
Hash财务报告和审定财务报表Digesthash 字节已经存在于内存中,没有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,它基于此软件包.