paulmillr/noble-ed25519

GitHub: paulmillr/noble-ed25519

一个仅 4KB 的高性能且易于审计的 JavaScript Ed25519 数字签名生成与验证库。

Stars: 510 | Forks: 67

# noble-ed25519 5KB 最快的 ed25519 签名 JS 实现。 - ✍️ 符合 [RFC8032](https://tools.ietf.org/html/rfc8032)、FIPS 186-5 的 [EDDSA](https://en.wikipedia.org/wiki/EdDSA) 签名 - 🪢 友好共识,符合 [ZIP215](https://zips.z.cash/zip-0215) - 🔖 SUF-CMA(在选择消息攻击下具有强不可伪造性)和 SBS(不可否认性 / 独占所有权) - 🪶 3.9KB(gzipped) 该模块是 [noble-curves](https://github.com/paulmillr/noble-curves) 的姊妹项目。 如果你需要更小的攻击面和更好的可审计性,请使用 noble-ed25519。 如果你需要诸如 ristretto255、x25519 / curve25519、ed25519ph、hash-to-curve、oprf 等功能,请切换到 noble-curves(可直接替换)。 ### 该库属于 _noble_ 密码学 - 零或极简依赖 - 高可读性的 TypeScript / JS 代码 - PGP 签名发布和透明的 NPM 构建 - 所有库: [ciphers](https://github.com/paulmillr/noble-ciphers)、 [curves](https://github.com/paulmillr/noble-curves)、 [hashes](https://github.com/paulmillr/noble-hashes)、 [post-quantum](https://github.com/paulmillr/noble-post-quantum)、 5kb [secp256k1](https://github.com/paulmillr/noble-secp256k1) / [ed25519](https://github.com/paulmillr/noble-ed25519) - WASM 版本:[awasm-noble](https://github.com/paulmillr/awasm-noble) - [查看主页](https://paulmillr.com/noble/) 以阅读资源、文档以及使用 noble 构建的应用 ## 用法 我们支持所有主要平台和运行时。对于 React Native,需要额外的 polyfill:见下文。 ``` import * as ed from '@noble/ed25519'; (async () => { const { secretKey, publicKey } = await ed.keygenAsync(); // const publicKey = await ed.getPublicKeyAsync(secretKey); const message = new TextEncoder().encode('hello noble'); const signature = await ed.signAsync(message, secretKey); const isValid = await ed.verifyAsync(signature, message, publicKey); })(); ``` ### 启用同步方法 默认情况下仅提供异步方法,以保持库无依赖。 要启用同步方法: ``` import * as ed from '@noble/ed25519'; import { sha512 } from '@noble/hashes/sha2.js'; ed.hashes.sha512 = sha512; // Sync methods can be used now: const { secretKey, publicKey } = ed.keygen(); const msg = new TextEncoder().encode('hello noble'); // const publicKey = ed.getPublicKey(secretKey); const sig = ed.sign(msg, secretKey); const isValid = ed.verify(sig, msg, publicKey); ``` ### React Native:polyfill getRandomValues 和 sha512 React Native 默认不提供安全的 getRandomValues。 这无法从我们这边进行安全的 polyfill,因此需要一个 RN 特定的编译时依赖。 ``` import 'react-native-get-random-values'; import * as ed from '@noble/ed25519'; import { sha512 } from '@noble/hashes/sha2.js'; ed.hashes.sha512 = sha512; ed.hashes.sha512Async = (m: Uint8Array) => Promise.resolve(sha512(m)); ``` ## API 共有 4 个主要方法,它们接受 Uint8Array 类型: - `keygen()` 和 `keygenAsync()` - `getPublicKey(secretKey)` 和 `getPublicKeyAsync(secretKey)` - `sign(message, secretKey)` 和 `signAsync(message, secretKey)` - `verify(signature, message, publicKey)` 和 `verifyAsync(signature, message, publicKey)` ### keygen ``` import * as ed from '@noble/ed25519'; import { sha512 } from '@noble/hashes/sha2.js'; ed.hashes.sha512 = sha512; (async () => { const keys = ed.keygen(); const { secretKey, publicKey } = keys; const keysA = await ed.keygenAsync(); })(); ``` ### getPublicKey ``` import * as ed from '@noble/ed25519'; import { sha512 } from '@noble/hashes/sha2.js'; ed.hashes.sha512 = sha512; (async () => { const { secretKey: secretKeyA } = ed.keygen(); const pubKey = ed.getPublicKey(secretKeyA); const pubKeyA = await ed.getPublicKeyAsync(secretKeyA); const pubKeyPoint = ed.Point.fromBytes(pubKey); const pubKeyExtended = ed.utils.getExtendedPublicKey(secretKeyA); })(); ``` 由 32 字节的私钥生成 32 字节的公钥。 - 一些库具有 64 字节的私钥 - 那只是私钥与公钥拼接的结果 - 如果你想将 hex 或 bytes 转换为 Point,请使用 `ExtendedPoint.fromHex(publicKey)`。 它将使用 RFC 8032 的解压缩算法 5.1.3。 - 如果你需要 seed 的完整 SHA512 哈希,请使用 `utils.getExtendedPublicKey` ### sign ``` import * as ed from '@noble/ed25519'; import { sha512 } from '@noble/hashes/sha2.js'; ed.hashes.sha512 = sha512; (async () => { const { secretKey, publicKey } = ed.keygen(); const message = new TextEncoder().encode('hello noble'); const signature = ed.sign(message, secretKey); const signatureA = await ed.signAsync(message, secretKey); })(); ``` 生成确定性 EdDSA 签名。`message` 会在 ed25519 内部进行哈希处理。 对于预哈希的 ed25519ph,请切换到 noble-curves。 ### verify ``` import * as ed from '@noble/ed25519'; import { sha512 } from '@noble/hashes/sha2.js'; ed.hashes.sha512 = sha512; (async () => { const { secretKey, publicKey } = ed.keygen(); const message = new TextEncoder().encode('hello noble'); const signature = ed.sign(message, secretKey); const isValid = ed.verify(signature, message, publicKey); const isValidFips = ed.verify(signature, message, publicKey, { zip215: false }); const isValidA = await ed.verifyAsync(signature, message, publicKey); })(); ``` 验证 EdDSA 签名。具有 SUF-CMA(在选择消息攻击下具有强不可伪造性)。 默认情况下,遵循 ZIP215 [^1],并且可用于共识关键型应用 [^2]。 `zip215: false` 选项将验证标准切换为严格的 RFC8032 / FIPS 186-5,并通过 SBS(强绑定签名)提供不可否认性 [^3]。 ### utils 此外还暴露了一组有用的**实用工具**: ``` import * as ed from '@noble/ed25519'; const { bytesToHex, hexToBytes, concatBytes, mod, invert, randomBytes } = ed.etc; const { getExtendedPublicKey, getExtendedPublicKeyAsync, randomSecretKey } = ed.utils; const { Point } = ed; console.log(Point.CURVE(), Point.BASE); /* class Point { static BASE: Point; static ZERO: Point; readonly X: bigint; readonly Y: bigint; readonly Z: bigint; readonly T: bigint; constructor(X: bigint, Y: bigint, Z: bigint, T: bigint); static CURVE(): EdwardsOpts; static fromAffine(p: AffinePoint): Point; static fromBytes(hex: Bytes, zip215?: boolean): Point; static fromHex(hex: string, zip215?: boolean): Point; get x(): bigint; get y(): bigint; assertValidity(): this; equals(other: Point): boolean; is0(): boolean; negate(): Point; double(): Point; add(other: Point): Point; subtract(other: Point): Point; multiply(n: bigint, safe?: boolean): Point; multiplyUnsafe(scalar: bigint): Point; toAffine(): AffinePoint; toBytes(): Bytes; toHex(): string; clearCofactor(): Point; isSmallOrder(): boolean; isTorsionFree(): boolean; } */ ``` ## 安全性 该模块已具备生产就绪状态。 我们与姊妹项目 [noble-curves](https://github.com/paulmillr/noble-curves) 进行了交叉测试,后者已经过审计并提供了更高的安全性。 - 当前版本尚未经过独立审计。它是 v1 的重写版本,v1 曾在 2022 年 2 月由 cure53 进行过审计: [PDF](https://cure53.de/pentest-report_ed25519.pdf)。 - 它正在[另一个单独的仓库](https://github.com/paulmillr/fuzzing)中进行模糊测试 如果你发现任何异常:请调查并报告。 ### 常数时间 我们的目标是实现算法层面的常数时间。_JIT 编译器_ 和 _垃圾回收器_ 使得在脚本语言中实现抗 [时序攻击](https://en.wikipedia.org/wiki/Timing_attack) 的“常数时间”变得极其困难。这意味着_任何其他 JS 库都无法做到常数时间_。即使是静态类型的 Rust(一种没有 GC 的语言),在某些情况下也[更难实现常数时间](https://www.chosenplaintext.ca/open-source/rust-timing-shield/security)。如果你的目标是绝对安全,请不要使用任何 JS 库 — 包括对原生库的绑定。请使用底层库和语言。 ### 供应链安全 - **提交** 使用 PGP 密钥签名以防伪造。请务必验证提交签名 - **发布** 通过无 token 的 GitHub CI 和 Trusted Publishing 透明进行。请务必验证[出处日志](https://docs.npmjs.com/generating-provenance-statements)以确认其真实性。 - 实行**低频发布**,以最大程度减少最终用户重新审计的需要。 - **依赖**被最小化并严格锁定,以降低供应链风险。 - 我们使用尽可能少的依赖。 - 版本范围已锁定,并使用 npm-diff 检查更改。 - **开发依赖**会从最终用户安装中排除;它们仅用于开发和构建步骤。 对于此包,有 0 个依赖;以及少数几个开发依赖: - [noble-hashes](https://github.com/paulmillr/noble-hashes) 提供加密哈希功能 - jsbt 用于基准测试 / 测试 / 构建工具,由同一作者开发 - prettier、fast-check 和 typescript 用于代码质量 / 测试生成 / TS 编译 ### 随机性 我们依赖内置的 [`crypto.getRandomValues`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues), 这被认为是加密安全的 PRNG。 浏览器过去曾出现过弱点——并且可能再次出现——但实现用户空间 CSPRNG 会更糟,因为没有可靠的用户空间高质量熵源。 ### 量子计算机 如果建造出具有密码学相关性的量子计算机,将能够使用 Shor 算法破解椭圆曲线密码学(ECDSA / EdDSA 和 ECDH 均包括在内)。 考虑切换到更新 / 混合的算法,例如 SPHINCS+。它们可在 [noble-post-quantum](https://github.com/paulmillr/noble-post-quantum) 中找到。 NIST 禁止在 [2035 年之后](https://nvlpubs.nist.gov/nistpubs/ir/2024/NIST.IR.8547.ipd.pdf)使用经典密码学(RSA、DSA、ECDSA、ECDH)。澳大利亚 ASD 禁止在 [2030 年之后](https://www.cyber.gov.au/resources-business-and-government/essential-cyber-security/ism/cyber-security-guidelines/guidelines-cryptography)使用。 ## 升级 ### v2 升级到 v3 v3 使该包更加接近 noble-curves v2。 - 大多数方法现在期望 Uint8Array,禁止输入字符串 hex - 添加了 `keygen`、`keygenAsync` 方法 - Node v20.19 现在是最低要求的版本 - 类型和 Point 类的各种小改动 - 等:哈希现在设置在 `hashes` 对象中: ``` import * as ed from '@noble/ed25519'; import { sha512 } from '@noble/hashes/sha2.js'; // before ed.etc.sha512Sync = (...m: Uint8Array[]) => sha512(ed.etc.concatBytes(...m)); ed.etc.sha512Async = (...m: Uint8Array[]) => Promise.resolve(sha512(ed.etc.concatBytes(...m))); // after ed.hashes.sha512 = sha512; ed.hashes.sha512Async = (m: Uint8Array) => Promise.resolve(sha512(m)); ``` ### v1 升级到 v2 v2 具有更高的安全性和更小的攻击面。 v2 的目标是提供尽可能小且安全、快速的 JS 库。 这意味着该库被缩减了 4 倍,仅剩 300 多行代码。为了实现 该目标,**一些功能被移至**了 [noble-curves](https://github.com/paulmillr/noble-curves),这是一个更安全、更快的直接替代库,且具有相同的 API。 如果你打算继续使用这些功能,请切换到 curves: - x25519 / curve25519 / getSharedSecret - ristretto255 / RistrettoPoint - 对非基点使用 `utils.precompute()` - 对不支持 bigint 字面量的环境的支持 - 对 Common.js 的支持 - 在没有 [shim](#usage) 的情况下对 node.js 18 及更早版本的支持 从 @noble/ed25519 1.7 升级到 2.0 的其他变更: - 方法现在默认是同步的;使用 `getPublicKeyAsync`、`signAsync`、`verifyAsync` 获取异步版本 - `getPublicKey`、`sign`、`verify` 中不再允许使用 `bigint`。原因:ed25519 是 LE(小端序),可能会导致 bug - `Point`(2d xy)已更改为 `ExtendedPoint`(xyzt) - `Signature` 已移除:现在只需使用原始 bytes 或 hex - `utils` 被拆分为 `utils`(与 noble-curves 中的 API 相同)和 `etc`(`sha512Sync` 及其他) ## 速度 ``` npm run bench ``` 基准测试使用 Apple M4 进行测量。 ``` init 11ms keygen x 14,467 ops/sec @ 69μs/op sign x 7,275 ops/sec @ 137μs/op verify x 2,004 ops/sec @ 498μs/op keygenAsync x 12,822 ops/sec @ 77μs/op signAsync x 5,902 ops/sec @ 169μs/op verifyAsync x 1,955 ops/sec @ 511μs/op Point.fromBytes x 36,545 ops/sec @ 27μs/op ``` 与替代实现的对比: ``` tweetnacl@1.0.3 getPublicKey x 1,808 ops/sec @ 552μs/op ± 1.64% tweetnacl@1.0.3 sign x 651 ops/sec @ 1ms/op ristretto255@0.1.2 getPublicKey x 640 ops/sec @ 1ms/op ± 1.59% sodium-native#sign x 83,654 ops/sec @ 11μs/op ``` ## 许可证 MIT 许可证 (MIT) 版权所有 (c) 2019 Paul Miller [(https://paulmillr.com)](https://paulmillr.com) 请参阅 LICENSE 文件。
标签:CMS安全, CVE, Ed25519, JavaScript, MITM代理, 加密库, 区块链, 密码学, 手动系统调用, 数字签名, 数据可视化, 自动化攻击