经过审计且极简的椭圆曲线密码学 JS 实现。
曲线有 5kb 的姊妹项目 secp256k1 和 ed25519。 它们攻击面更小,但功能也更少。
noble cryptography — 高安全性、易于审计的独立密码学库与工具集。
npm install @noble/curves
deno add jsr:@noble/curves
我们支持所有主流平台和运行时。 对于 React Native,你可能需要 getRandomValues 的 polyfill。 也提供独立文件 noble-curves.js。```js // import * from '@noble/curves'; // Error: use sub-imports, to ensure small app size import { secp256k1 } from '@noble/curves/secp256k1.js'; const { secretKey, publicKey } = secp256k1.keygen(); const msg = new TextEncoder().encode('hello noble'); const sig = secp256k1.sign(msg, secretKey); const isValid = secp256k1.verify(sig, msg, publicKey);
- [ECDSA、EdDSA、Schnorr 签名](#ecdsa-eddsa-schnorr-signatures)
- [ECDH:Diffie-Hellman 共享密钥](#ecdh-diffie-hellman-shared-secrets)
- [webcrypto:友好封装](#webcrypto-friendly-wrapper)
- [BLS 签名、bls12-381、bn254 又名 alt\_bn128](#bls-signatures-bls12-381-bn254-aka-alt_bn128)
- [hash-to-curve:哈希到曲线点](#hash-to-curve-hashing-to-curve-points)
- [OPRF](#oprfs) | [FROST 门限签名](#frost-threshold-signatures)
- [poseidon:Poseidon 哈希](#poseidon-poseidon-hash) | [fft:快速傅里叶变换](#fft-fast-fourier-transform) | [utils](#utils-byte-shuffling-conversion)
- 内部实现:[点运算](#elliptic-curve-point-math) | [模运算](#modular-modular-arithmetics--finite-fields) | [自定义曲线](#weierstrass-custom-weierstrass-curve--ecdsa)
- [规范](#specs)
- [安全性](#security) | [速度](#speed) | [升级](#upgrading) | [贡献与测试](#contributing--testing) | [许可证](#license)
### ECDSA、EdDSA、Schnorr 签名
#### secp256k1、p256、p384、p521、ed25519、ed448、brainpool```js
import { secp256k1, schnorr } from '@noble/curves/secp256k1.js';
import { p256, p384, p521 } from '@noble/curves/nist.js';
import { ed25519 } from '@noble/curves/ed25519.js';
import { ed448 } from '@noble/curves/ed448.js';
import { brainpoolP256r1, brainpoolP384r1, brainpoolP512r1 } from '@noble/curves/misc.js';
for (const curve of [
secp256k1, schnorr,
p256, p384, p521,
ed25519, ed448,
brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
]) {
const { secretKey, publicKey } = curve.keygen();
const msg = new TextEncoder().encode('hello noble');
const sig = curve.sign(msg, secretKey);
const isValid = curve.verify(sig, msg, publicKey);
console.log(curve, secretKey, publicKey, sig, isValid);
}
// Specific private key
import { hexToBytes } from '@noble/curves/utils.js';
const secret2 = hexToBytes('46c930bc7bb4db7f55da20798697421b98c4175a52c630294d75a84b9c126236');
const pub2 = secp256k1.getPublicKey(secret2);
消息始终先进行哈希:参见预哈希签名。 ECDSA 使用确定性 k,EdDSA 遵循 RFC 8032,Schnorr(仅限 secp256k1)遵循 BIP 340:参见规范。
MuSig2 签名方案以及用于 secp256k1 的 BIP324 ElligatorSwift 映射 可在单独的包中获取。
import { ristretto255, ristretto255_hasher, ristretto255_oprf } from '@noble/curves/ed25519.js'; import { decaf448, decaf448_hasher, decaf448_oprf } from '@noble/curves/ed448.js';
console.log(ristretto255.Point, decaf448.Point);
查看 [RFC 9496](https://www.rfc-editor.org/rfc/rfc9496) 以获取有关 ristretto255 和 decaf448 的更多信息。
查看关于 [Point](#elliptic-curve-point-math)、[hasher](#hash-to-curve-hashing-to-curve-points) 和 [oprf](#oprfs) 的单独文档。
#### 预哈希签名```js
import { secp256k1 } from '@noble/curves/secp256k1.js';
import { keccak_256 } from '@noble/hashes/sha3.js';
const { secretKey } = secp256k1.keygen();
const msg = new TextEncoder().encode('hello noble');
// prehash: true (default) - hash using secp256k1.hash (sha256)
const sig = secp256k1.sign(msg, secretKey);
// prehash: false - hash using custom hash
const sigKeccak = secp256k1.sign(keccak_256(msg), secretKey, { prehash: false });
默认情况下(prehash: true),sign() 和 verify() 会先对消息应用曲线内置的哈希:
secp256k1 使用 sha256,p521 使用 sha512。prehash: false 允许使用自定义哈希
(例如 secp256k1 + keccak_256)。在 noble-curves v1 中,prehash: false 是默认值。
import { secp256k1 } from '@noble/curves/secp256k1.js'; const { secretKey, publicKey } = secp256k1.keygen(); const msg = new TextEncoder().encode('hello noble'); const sigRec = secp256k1.sign(msg, secretKey, { format: 'recovered' }); const publicKey_ = secp256k1.recoverPublicKey(sigRec, msg); // == publicKey
// recovered sig is compact sig with an extra byte const sigNoRec = secp256k1.sign(msg, secretKey, { format: 'compact' }); // sigNoRec == sigRec.slice(1)
// Signature instance const sigInstance = secp256k1.Signature.fromBytes(sigRec, 'recovered');
公钥恢复仅支持 ECDSA。这是一个简单的数学运算:
无法保证签名确实已完成。伪造的 (r, s, h) 会恢复出一个
随机公钥,但要找到能导致这个特定伪造 h 的 m 是不可行的。
#### 带噪声的对冲 ECDSA```js
import { secp256k1 } from '@noble/curves/secp256k1.js';
const { secretKey } = secp256k1.keygen();
const msg = new TextEncoder().encode('hello noble');
// extraEntropy: false - default, hedging disabled
const sigNoisy = secp256k1.sign(msg, secretKey);
// extraEntropy: true - fetch 32 random bytes from CSPRNG
const sigNoisyA = secp256k1.sign(msg, secretKey, { extraEntropy: true });
// extraEntropy: bytes - specific extra entropy
const ent = Uint8Array.from([0xca, 0xfe, 0x01, 0x23]);
const sigNoisy2 = secp256k1.sign(msg, secretKey, { extraEntropy: ent });
默认情况下,ECDSA 签名是确定性的(RFC 6979)。纯确定性签名容易受到故障攻击,因此较新的方案(如 BIP340 schnorr)在签名生成中引入了随机性——也称为对冲。extraEntropy 启用对冲模式。更多信息,请查看确定性签名不是你的朋友。
import { ed25519 } from '@noble/curves/ed25519.js'; const { secretKey, publicKey } = ed25519.keygen(); const msg = new TextEncoder().encode('hello noble'); const sig = ed25519.sign(msg, secretKey); // zip215: true const isValid = ed25519.verify(sig, msg, publicKey); // SBS / e-voting / RFC8032 / FIPS 186-5 const isValidRfc = ed25519.verify(sig, msg, publicKey, { zip215: false });
* `zip215: true`(默认)使用 [ZIP215](https://zips.z.cash/zip-0215) 中定义的更宽松、[共识友好](https://hdevalence.ca/blog/2020-10-04-its-25519am)的验证规则。
* `zip215: false` 强制执行严格的 RFC 8032 / FIPS 186-5 验证,并添加基于 SBS 的
不可否认性,这对于合同签署、电子投票和区块链非常有用。
两种模式都具有 SUF-CMA(选择消息攻击下的强不可伪造性);
大多数其他库既没有 SUF-CMA 也没有 SBS。
更多信息请参见 [Taming the many EdDSAs](https://eprint.iacr.org/2020/1244)。
### ECDH:Diffie-Hellman 共享密钥```js
import { x25519 } from '@noble/curves/ed25519.js';
const alice = x25519.keygen();
const bob = x25519.keygen();
const sharedKey = x25519.getSharedSecret(alice.secretKey, bob.publicKey);
// Same API: secp256k1, p256, p384, p521, x448
// converting ed25519 keys to x25519
import { ed25519 } from '@noble/curves/ed25519.js';
const alice2 = ed25519.keygen();
const bob2 = ed25519.keygen();
const aliceSecX = ed25519.utils.toMontgomerySecret(alice2.secretKey);
const bobPubX = ed25519.utils.toMontgomery(bob2.publicKey);
const sharedKey2 = x25519.getSharedSecret(aliceSecX, bobPubX);
我们为所有 Weierstrass 曲线以及 2 条 Montgomery 曲线 X25519(Curve25519)和 X448(Curve448)提供 ECDH,符合 RFC 7748。
在 Weierstrass 曲线中,共享密钥:
key.slice(1) 将其剥离sha256(shared) 或 hkdf(shared)import { ed25519, x25519 } from '@noble/curves/webcrypto.js';
// signatures: p256, p384, p521, ed25519, ed448 const keys = await ed25519.keygen(); const msg = new TextEncoder().encode('hello noble'); const sig = await ed25519.sign(msg, keys.secretKey); const isValid = await ed25519.verify(sig, msg, keys.publicKey);
// ECDH: p256, p384, p521, x25519, x448 const alice = await x25519.keygen(); const bob = await x25519.keygen(); const shared = await x25519.getSharedSecret(alice.secretKey, bob.publicKey);
// key conversion between noble (raw) and webcrypto (pkcs8 / spki) formats import { p256 as p256n } from '@noble/curves/nist.js'; import { p256 } from '@noble/curves/webcrypto.js'; const nobleKeys = p256n.keygen(); const secretKeyPkcs8 = await p256.utils.convertSecretKey(nobleKeys.secretKey, 'raw', 'pkcs8'); const publicKeySpki = await p256.utils.convertPublicKey(nobleKeys.publicKey, 'raw', 'spki');
一个对内置 WebCrypto 的轻量封装,镜像 noble API。方法始终为异步;
运行时支持情况各异——使用 `await curve.isSupported()` 进行检查。
查看 [micro-key-producer](https://github.com/paulmillr/micro-key-producer) 获取
纯 JS 密钥转换工具。
### BLS 签名、bls12-381、bn254 又名 alt_bn128```ts
import { bls12_381 } from '@noble/curves/bls12-381.js';
// G1 pubkeys, G2 sigs
const blsl = bls12_381.longSignatures;
const { secretKey, publicKey } = blsl.keygen();
const msg = new TextEncoder().encode('hello noble');
const msgp = blsl.hash(msg); // hash to point, default DST
const msgpd = blsl.hash(msg, 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_'); // custom DST (Ethereum)
const signature = blsl.sign(msgp, secretKey);
const isValid = blsl.verify(signature, msgp, publicKey);
// G1 sigs, G2 pubkeys: identical API
const blss = bls12_381.shortSignatures;
// Aggregation
const aggregatedKey = blsl.aggregatePublicKeys([
blsl.getPublicKey(bls12_381.utils.randomSecretKey()),
blsl.getPublicKey(bls12_381.utils.randomSecretKey()),
]);
// const aggregatedSig = blsl.aggregateSignatures(sigs)
// Pairings: bls12_381.pairing(PointG1, PointG2)
// Fields: bls12_381.fields.Fp, Fp2, Fp12, Fr
例如用法,请查看 BLS EVM 预编译的实现。
BN254 API 与 bls12-381 类似。该曲线之前被称为 alt_bn128。 该实现兼容 EIP-196 和 EIP-197: 请查看 bn254 EVM 预编译的实现。 bn254 点不实现 toBytes,因为没有序列化标准: 不同实现在字节序、标志和 G2 虚部顺序上存在分歧。 请改为从 bigint 初始化点。
import { secp256k1_hasher } from '@noble/curves/secp256k1.js';
const msg = Uint8Array.from([0xca, 0xfe, 0x01, 0x23]); const point = secp256k1_hasher.hashToCurve(msg); const pointDst = secp256k1_hasher.hashToCurve(msg, { DST: 'hello noble' }); const pointNu = secp256k1_hasher.encodeToCurve(msg); const scalar = secp256k1_hasher.hashToScalar(msg);
// Same API: p256_hasher, p384_hasher, p521_hasher (nist.js), // ed25519_hasher, ristretto255_hasher (ed25519.js), ed448_hasher, decaf448_hasher (ed448.js), // bls12_381.G1, bls12_381.G2. // ristretto255 & decaf448 also provide deriveToCurve.
// abstract methods import { expand_message_xmd, expand_message_xof, hash_to_field } from '@noble/curves/abstract/hash-to-curve.js';
该模块允许将任意字符串哈希到椭圆曲线点。实现了 [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380)。
`_hasher` 命名空间与曲线分离,以便进行 tree-shaking:
不需要 hash-to-curve 的用户不会在构建中包含它。
### OPRFs```js
import { p256_oprf, p384_oprf, p521_oprf } from '@noble/curves/nist.js';
import { ristretto255_oprf } from '@noble/curves/ed25519.js';
import { decaf448_oprf } from '@noble/curves/ed448.js';
我们提供符合 RFC 9497 的 OPRF(不经意伪随机函数)。
OPRF 允许交互式地创建 Output = PRF(Input, serverSecretKey):
FROST 实现了 RFC 9591 门限 Schnorr 签名。
从应用角度看,它类似于多重签名:max 个参与者中的任意 min 个可以共同在共享公钥下生成一个 Schnorr 签名。
支持的密码套件有 p256_FROST、ed25519_FROST、ed448_FROST、ristretto255_FROST、secp256k1_FROST 和 schnorr_FROST(兼容 Taproot 的 secp256k1)。
签名分为两轮:选定的签名者先提交承诺,然后生成签名份额。```js
import { p256_FROST } from '@noble/curves/nist.js';
const signers = { min: 2, max: 3 }; const alice = p256_FROST.Identifier.derive('[email protected]'); const bob = p256_FROST.Identifier.derive('[email protected]'); const carol = p256_FROST.Identifier.derive('[email protected]'); // trusted dealer const deal = p256_FROST.trustedDealer(signers, [alice, bob, carol]); for (const id of [alice, bob, carol]) p256_FROST.validateSecret(deal.secretShares[id], deal.public);
const msg = new TextEncoder().encode('hello threshold'); // round 1: selected signers commit const aliceRound1 = p256_FROST.commit(deal.secretShares[alice]); const bobRound1 = p256_FROST.commit(deal.secretShares[bob]); const commitmentList = [aliceRound1.commitments, bobRound1.commitments]; // round 2: signers produce signature shares const sigShares = { [alice]: https://raw.githubusercontent.com/paulmillr/noble-curves/main/p256_FROST.signShare( deal.secretShares[alice], deal.public, aliceRound1.nonces, commitmentList, msg ), [bob]: https://raw.githubusercontent.com/paulmillr/noble-curves/main/p256_FROST.signShare( deal.secretShares[bob], deal.public, bobRound1.nonces, commitmentList, msg ), }; const sig = p256_FROST.aggregate(deal.public, commitmentList, msg, sigShares); const isValid = p256_FROST.verify(sig, msg, deal.public.commitments[0]);
密钥生成可以通过可信经销商(如上所述)或使用 DKG(分布式密钥生成)来完成。
DKG 有三个轮次:参与者对密钥生成进行承诺,交换私有份额,
然后推导出最终的参与者密钥——参见
[测试](https://github.com/paulmillr/noble-curves/blob/main/test/rfc9591-frost.test.ts) 中 `DKG.round1` / `round2` / `round3` 的用法。
该库实现的是密码学步骤,而非外围的应用协议:
调用方仍然需要经过身份验证的通信、协调、重试、会话处理以及策略。
### poseidon:Poseidon 哈希
实现了 [Poseidon](https://www.poseidon-hash.info) 这一对 ZK 友好的哈希:
置换和海绵结构。
存在许多具有不同常量的 poseidon 变体。
我们不提供它们:你应该手动构造它们。
请查看 [scure-starknet](https://github.com/paulmillr/scure-starknet) 包以获取一个合适的示例。```ts
import { bn254 } from '@noble/curves/bn254.js';
import { grainGenConstants, poseidon, poseidonSponge } from '@noble/curves/abstract/poseidon.js';
const rate = 2;
const capacity = 1;
const Fp = bn254.fields.Fr;
const { mds, roundConstants } = grainGenConstants({
Fp,
t: rate + capacity,
roundsFull: 8,
roundsPartial: 31,
});
const opts = {
Fp,
rate,
capacity,
sboxPower: 17,
mds,
roundConstants,
roundsFull: 8,
roundsPartial: 31,
};
const permutation = poseidon({ ...opts, t: rate + capacity });
const sponge = poseidonSponge(opts); // use carefully, not specced
import * as fft from '@noble/curves/abstract/fft.js'; import { bls12_381 } from '@noble/curves/bls12-381.js'; const Fr = bls12_381.fields.Fr; const roots = fft.rootsOfUnity(Fr, 7n); const fftFr = fft.FFT(roots, Fr);
有限域上的 NTT / FFT(快速傅里叶变换)。
### utils:字节混洗、转换```ts
import { bytesToHex, concatBytes, equalBytes, hexToBytes } from '@noble/curves/utils.js';
bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23]));
hexToBytes('cafe0123');
concatBytes(Uint8Array.from([0xca, 0xfe]), Uint8Array.from([0x01, 0x23]));
equalBytes(Uint8Array.of(0xca), Uint8Array.of(0xca));
import { secp256k1 } from '@noble/curves/secp256k1.js'; const { Point } = secp256k1; const { BASE, ZERO, Fp, Fn } = Point; const p = BASE.multiply(2n);
// Math const p1 = p.add(p); const p2 = p.double(); const p3 = p.subtract(p); const p4 = p.negate(); const p5 = p.multiply(451n);
// MSM (multi-scalar multiplication) import { pippenger } from '@noble/curves/abstract/curve.js'; const pa = [BASE, BASE.multiply(2n), BASE.multiply(4n), BASE.multiply(8n)]; const p6 = pippenger(Point, pa, [3n, 5n, 7n, 11n]); // == BASE.multiply(129n)
// Cofactor const pcl = p.clearCofactor(); const isTorsionFree = p.isTorsionFree();
// Conversions const bytes = p.toBytes(); const p_ = Point.fromBytes(bytes); const { x, y } = p.toAffine(); const p__ = Point.fromAffine({ x, y });
每条曲线都暴露其 Point 类:secp256k1、schnorr、p256、p384、p521、ed25519、ed448、
ristretto255、decaf448、bls12_381.G1 / G2、bn254.G1、jubjub、babyjubjub。
Weierstrass 点使用射影(齐次)坐标 `new Point(X, Y, Z)`,
edwards 点使用扩展坐标 `new Point(X, Y, Z, T)`;两者均满足 x=X/Z, y=Y/Z。
#### modular:模运算与有限域```js
import { mod, invert, Field } from '@noble/curves/abstract/modular.js';
// Finite Field utils
const fp = Field(2n ** 255n - 19n); // Finite field over 2^255-19
fp.mul(591n, 932n); // multiplication
fp.pow(481n, 11024858120n); // exponentiation
fp.div(5n, 17n); // division: 5/17 mod 2^255-19 == 5 * invert(17)
fp.inv(5n); // modular inverse
fp.sqrt(4n); // square root
// Non-Field generic utils are also available
mod(21n, 10n); // 21 mod 10 == 1n; fixed version of 21 % 10
invert(17n, 10n); // invert(17) mod 10; modular multiplicative inverse
所有算术运算均基于有限域上的 JS bigints 进行,
该有限域由 modular 子模块定义。
域运算并非恒定时间:参见安全性。
这一事实大多无关紧要,但需要牢记的重要方法是 pow,
在朴素使用时,它可能会泄露指数位。
import { weierstrass, ecdsa } from '@noble/curves/abstract/weierstrass.js'; import { sha256 } from '@noble/hashes/sha2.js'; // NIST secp192r1 aka p192. https://www.secg.org/sec2-v2.pdf const p192_CURVE = { p: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn, n: 0xffffffffffffffffffffffff99def836146bc9b1b4d22831n, h: 1n, a: 0xfffffffffffffffffffffffffffffffefffffffffffffffcn, b: 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1n, Gx: 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012n, Gy: 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811n, }; const p192_Point = weierstrass(p192_CURVE); const p192 = ecdsa(p192_Point, sha256);
const keys = p192.keygen(); const msg = new TextEncoder().encode('custom curve'); const sig = p192.sign(msg, keys.secretKey); const isValid = p192.verify(sig, msg, keys.publicKey);
Short Weierstrass 曲线的公式为 `y² = x³ + ax + b`。`weierstrass`
期望参数 `a`、`b`、域特征 `p`、曲线阶 `n`、
余因子 `h` 以及生成点的坐标 `Gx`、`Gy`,并返回一个 Point 类。
`ecdsa` 将 Point 类与哈希函数结合成一个签名方案。
#### edwards:自定义 Edwards 曲线```js
import { edwards } from '@noble/curves/abstract/edwards.js';
const ed25519_CURVE = {
p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
h: 8n,
a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,
};
const ed25519_Point = edwards(ed25519_CURVE);
Twisted Edwards 曲线的公式为 ax² + y² = 1 + dx²y²。
你必须指定 a、d、域特征 p、曲线阶 n(有时称为 L)、
余因子 h 以及生成元点的坐标 Gx、Gy。
该库已经过审计:
curve、modular、poseidon、weierstrass我们已于 2026 年 4 月开始定期进行 AI 辅助的自审计。
它已针对基于属性的、跨库的和 Wycheproof 向量进行了测试, 并正在 GitHub CI 中进行模糊测试。
如果你发现任何异常:请调查并报告。
我们的目标是算法层面的常量时间。JIT 编译器 和 垃圾回收器 使得在脚本语言中 极难实现时序攻击抗性。 这意味着 任何其他 JS 库都无法具备常量时间性。即使是静态类型、没有 GC 的 Rust, 在某些情况下也更难实现常量时间。 如果你的目标是绝对安全,请不要使用任何 JS 库——包括对原生库的绑定。 请使用底层库和语言。
在这些限制内,秘密标量乘法提供了具体、可测量的特性:
multiply() 使用带符号固定窗口表,
并进行数据无关的表扫描——点操作的数量和顺序
与标量值无关。r 掩码为 s + r·n。这适用于余因子为 1 的曲线
(p256、p384、p521、secp256k1)上的所有乘法,以及所有地方的基点乘法。benchmark/ct.ts)
比较对抗性标量类别(稀疏 vs 密集、低位 vs 高位、
接近阶、位模式)之间的时序。基点乘法在任何曲线上
都没有显示出可区分的时序,随机点乘法在 Weierstrass 曲线上
也没有显示出可区分的时序(1000 个样本时最大 |t| ≤ 2.8;阈值为 4.5)。已知限制:在带余因子的 Edwards 曲线(ed25519、ed448)上,将非基点 点与秘密标量相乘时没有进行盲化。同一工具可以可靠地检测到这一点。EdDSA 签名 不受影响(它只乘以经过盲化的基点),而 X25519/X448 使用单独的 Montgomery 阶梯实现(同样不受影响)。这对于那些将任意 Edwards/Ristretto 点与长期存活的秘密标量相乘的协议很重要;在那里应优先使用 按构造即为全宽的标量。请注意,在孤立工具中的可检测性并不 意味着实际可利用性:我们在现实的 跨租户 / 浏览器内环境中尝试提取标量,即使使用 100,000 个时序样本, 也无法恢复 Edwards 标量。
如果你的目标是绝对安全,请使用底层语言而不是 JS / WASM。
该库主要使用 Uint8Array 和 bigint。
.fill(0),它会指示将内容填充为零,
但在 JS 中没有保证await fn() 会将所有内部变量写入内存。对于
异步函数,无法保证代码块何时
被执行。这意味着攻击者可以有
充足的时间从内存中读取数据。这意味着某些秘密可能会比预期更久地留在内存中。 然而,如果攻击者能够读取应用程序内存,那无论如何都完了: 如果不使用复杂的测试套件来转储进程内存并验证 没有敏感数据残留,就无法保证任何关于清零敏感数据的事情。 对于 JS 来说,这意味着要测试所有浏览器(包括移动端)。 当然,如果不在实际使用该库的应用程序中使用相同的 测试套件,那也将毫无用处。
对于此包,有 1 个依赖;以及少量开发依赖:
我们依赖内置的
crypto.getRandomValues,
它被认为是加密安全的 PRNG。
浏览器过去曾有过弱点——将来可能还会——但实现用户空间 CSPRNG 更糟,因为不存在可靠的用户空间高质量熵源。
与密码学相关的量子计算机如果被建造出来,将能够 使用 Shor 算法破解椭圆曲线密码学(ECDSA / EdDSA 和 ECDH)。
考虑切换到更新 / 混合算法,例如 SPHINCS+。它们可在 noble-post-quantum 中获得。
NIST 禁止经典密码学(RSA、DSA、ECDSA、ECDH)在 2035 年之后。澳大利亚 ASD 禁止其在 2030 年之后。
npm run benchmark
noble-curves 需要花费 10 毫秒以上来生成 20MB 以上的基点预计算数据。
此操作对每条曲线**仅执行一次**。
生成过程会延迟到任何方法(pubkey、sign、verify)被调用时才进行。
用户可以通过手动调用 `Point.BASE.precompute(windowSize, false)` 来强制生成预计算数据。
请查看源代码。
在 Apple M4 上的基准测试结果:```
# algorithm=getPublicKey
ed25519 7,299 ops/sec · 137 μs/op
secp256k1 4,872 ops/sec · 205 μs/op · -1.5x
p256 4,724 ops/sec · 212 μs/op · -1.5x
bls12_381 (long, G2 sig) 3,466 ops/sec · 288 μs/op · -2.1x
ed448 3,224 ops/sec · 310 μs/op · -2.3x
p384 2,185 ops/sec · 458 μs/op · -3.3x
p521 1,221 ops/sec · 819 μs/op · -6x
bls12_381 (short, G1 sig) 1,070 ops/sec · 934 μs/op · -6.8x
# algorithm=sign
secp256k1 4,217 ops/sec · 237 μs/op
p256 4,116 ops/sec · 243 μs/op · ≈
ed25519 3,536 ops/sec · 283 μs/op · -1.2x
p384 1,992 ops/sec · 502 μs/op · -2.1x
ed448 1,577 ops/sec · 634 μs/op · -2.7x
p521 1,131 ops/sec · 884 μs/op · -3.7x
bls12_381 (short, G1 sig) 417 ops/sec · 2.39 ms/op · -10x
bls12_381 (long, G2 sig) 112 ops/sec · 8.88 ms/op · -37x
# algorithm=verify
ed25519 1,504 ops/sec · 665 μs/op
secp256k1 1,352 ops/sec · 739 μs/op · -1.1x
p256 917 ops/sec · 1.09 ms/op · -1.6x
ed448 546 ops/sec · 1.83 ms/op · -2.8x
p384 381 ops/sec · 2.62 ms/op · -3.9x
p521 187 ops/sec · 5.34 ms/op · -8x
bls12_381 (short, G1 sig) 100 ops/sec · 9.98 ms/op · -15x
bls12_381 (long, G2 sig) 77 ops/sec · 12.9 ms/op · -19x
# algorithm=getSharedSecret
ed25519 1,695 ops/sec · 590 μs/op
secp256k1 763 ops/sec · 1.31 ms/op · -2.2x
p256 737 ops/sec · 1.36 ms/op · -2.3x
ed448 599 ops/sec · 1.67 ms/op · -2.8x
p384 326 ops/sec · 3.06 ms/op · -5.2x
p521 176 ops/sec · 5.68 ms/op · -9.6x
支持的 node.js 版本:
v2 大幅简化了内部实现,提升了安全性,减小了打包体积,并为未来铺平了道路。 我们尽可能保持 v2 向后兼容。
升级路径: 先升级到 curves v1.9.x。修复弃用警告,然后切换到 v2。
模块:
.js 扩展名:@noble/curves/ed25519 => @noble/curves/ed25519.js。
这使得无需转译器即可在浏览器中原生使用p256、p384、p521 已移至 nist;jubjub 已移至 miscpasta 和 bn254_weierstrass(非基于配对的 bn254)曲线已被移除新特性:
isValidSecretKey、isValidPublicKey 方法破坏性变更:
Point.fromHex 现在仅接受字符串:对于 Uint8Array 请使用 Point.fromBytes{prehash: false}{lowS: false}{format: 'der'}。
这减少了可延展性signature.toBytes()longSignatures(G1 公钥,G2 签名)和 shortSignatures(G1 签名,G2 公钥){message: ..., publicKey: ...}[]weierstrass() + ecdsa() / edwards() + eddsa()。
weierstrass / edwards 期望简化的曲线参数(Fp 变为 p);
ecdsa / eddsa 期望 Point 类和哈希重命名(curves v1.9 将旧名称标记为已弃用):
abstract/curve.js 子模块中的独立方法Point.BASE.multiply(Point.Fn.fromBytes(key))CURVE 属性 => Point.CURVE(),
仅提供曲线参数*curve*_hasher。
例如:secp256k1.hashToCurve => secp256k1_hasher.hashToCurve()已移除的特性:Point#multiplyAndAddUnsafe、Point#hasEvenY、Field.MASK
npm install && npm run build && npm test 将构建代码并运行测试。
还有额外的测试套件:慢速大标量 / 大曲线测试 npm run test:slow,
以及常量时间测试框架 npm run benchmark:ct。
有关该库的有用资源、文章、文档和演示,请参见 paulmillr.com/noble。
MIT 许可证(MIT)
版权所有 (c) 2022 Paul Miller (https://paulmillr.com)
参见 LICENSE 文件。
modularposeidonutilsweierstrass_shortw_utilssecp256k1