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代理, 加密库, 区块链, 密码学, 手动系统调用, 数字签名, 数据可视化, 自动化攻击