
تنفيذ JavaScript مُدقَّق ومُصغَّر لتشفير المنحنيات الإهليلجية.
تطبيق JS مُدقَّق ومصغَّر لتشفير المنحنيات الإهليلجية.
للمنحنيات مشاريع شقيقة بحجم 5kb secp256k1 و ed25519. لديها سطح هجوم أصغر، لكن ميزات أقل.
noble cryptography — مجموعة عالية الأمان وقابلة للتدقيق بسهولة من مكتبات وأدوات التشفير المُحتواة.
npm install @noble/curves
deno add jsr:@noble/curves
ندعم جميع المنصات وبيئات التشغيل الرئيسية. بالنسبة لـ React Native، قد تحتاج إلى polyfill لـ getRandomValues. ملف مستقل 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)
- [OPRFs](#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-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 وتعيين BIP324 ElligatorSwift لمنحنى secp256k1 متاحان في حزمة منفصلة.
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() التجزئة المدمجة للمنحنى على الرسالة أولاً:
sha256 لـ secp256k1، و sha512 لـ p521. يسمح 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) المزوّر يُستعاد إلى
مفتاح عام عشوائي، لكن ليس من الممكن إيجاد m الذي سيؤدي إلى هذا h المزوّر المحدد.
#### 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` (افتراضي) يستخدم قواعد التحقق الأكثر تسامحًا و[المتوافقة مع الإجماع](https://hdevalence.ca/blog/2020-10-04-its-25519am) المعرّفة في [ZIP215](https://zips.z.cash/zip-0215).
* `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);
نوفر ECDH على جميع منحنيات Weierstrass، وعلى منحنيي Montgomery X25519 (Curve25519) و X448 (Curve448)، وفقًا لـ 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. الطرق دائمًا غير متزامنة (async)؛
يختلف الدعم حسب بيئة التشغيل - تحقق باستخدام `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 precompiles.
واجهة BN254 تحاكي bls12-381. كان يُطلق على المنحنى سابقًا alt_bn128. التنفيذ متوافق مع EIP-196 و EIP-197: تحقق من تنفيذ bn254 EVM precompiles. نقاط bn254 لا تنفذ toBytes، لأنه لا يوجد معيار للتسلسل: تختلف التنفيذات في endianness والأعلام وترتيب الجزء التخيلي G2. قم بتهيئة النقاط من bigints بدلاً من ذلك.
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';
نوفر OPRFs (دوال شبه عشوائية لا واعية)، متوافقة مع RFC 9497.
تتيح OPRF إنشاء Output = PRF(Input, serverSecretKey) بشكل تفاعلي:
تنفذ FROST توقيع Schnorr العتبي وفق RFC 9591.
وهو مشابه للتوقيع المتعدد من منظور التطبيق: أي min من max مشارك
يمكنهم معًا إنتاج توقيع 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 ثلاث جولات: يلتزم المشاركون بتوليد المفاتيح، ويتبادلون الحصص الخاصة،
ثم يشتقون مفاتيح المشاركين النهائية - راجع استخدام `DKG.round1` / `round2` / `round3` في
[الاختبارات](https://github.com/paulmillr/noble-curves/blob/main/test/rfc9591-frost.test.ts).
تنفّذ المكتبة الخطوات التشفيرية، وليس بروتوكول التطبيق المحيط:
لا يزال على المستدعين توفير الاتصال الموثّق، والتنسيق، وإعادة المحاولات، وإدارة الجلسات، والسياسة.
### 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
كل العمليات الحسابية تتم باستخدام أعداد صحيحة كبيرة في JavaScript (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);
صيغة منحنى 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.
يتم اختبارها مقابل متجهات قائمة على الخصائص، وعبر المكتبات، ومتجهات Wycheproof، ويتم إجراء اختبار fuzzing عليها في github ci.
إذا رأيت أي شيء غير عادي: حقّق وأبلغ.
نستهدف زمنًا خوارزميًا ثابتًا. مترجم JIT و_جامع القمامة_ يجعلان تحقيق مقاومة هجوم التوقيت "بالزمن الثابت" أمرًا بالغ الصعوبة في لغة برمجة نصية. مما يعني أن أي مكتبة JS أخرى لا يمكنها تحقيق ثبات التوقيت. حتى Rust ذات الكتابة الساكنة، وهي لغة بدون جامع قمامة، تجعل تحقيق الزمن الثابت أصعب في بعض الحالات. إذا كان هدفك هو الأمان المطلق، فلا تستخدم أي مكتبة JS — بما في ذلك الارتباطات بالمكتبات الأصلية. استخدم مكتبات ولغات منخفضة المستوى.
ضمن هذه الحدود، يوفر ضرب العدد القياسي السري خصائص محددة وقابلة للقياس:
multiply() جداول نوافذ ثابتة موقّعة مع
مسح جداول غير معتمد على البيانات — عدد وترتيب عمليات النقاط
مستقل عن قيمة العدد القياسي.s + r·n باستخدام r عشوائي
بطول 128 بت قبل الضرب. ينطبق هذا على جميع عمليات الضرب على منحنيات
العامل المرافق 1 (p256، p384، p521، secp256k1)، وعلى عمليات ضرب نقطة الأساس في كل مكان.benchmark/ct.ts)
تقارن التوقيت عبر فئات العداد القياسي العدائية (متناثر مقابل كثيف، بتات منخفضة مقابل عالية،
قريب من الترتيب، أنماط بتات). يُظهر ضرب نقطة الأساس عدم وجود توقيت مميز على أي
منحنى، ويُظهر ضرب النقطة العشوائية عدم وجوده على منحنيات Weierstrass
(أقصى |t| ≤ 2.8 عند 1000 عينة؛ العتبة 4.5).قيد معروف: على منحنيات Edwards ذات العامل المرافق (ed25519، ed448)، فإن ضرب نقطة غير أساسية بعدد قياسي سري لا يتم تعميته. تكتشف الأداة نفسها هذا بشكل موثوق. توقيع EdDSA غير متأثر (فهو يضرب فقط نقطة الأساس المعمّاة)، ويستخدم X25519/X448 تطبيق Montgomery-ladder منفصل (غير متأثر أيضًا). يهم هذا للبروتوكولات التي تضرب نقاط Edwards/Ristretto عشوائية بعدادات قياسية سرية طويلة الأمد؛ يُفضّل هناك استخدام عدادات قياسية كاملة العرض بالبناء. لاحظ أن القابلية للكشف في أداة اختبار معزولة لا تعني قابلية استغلال عملية: حاولنا استخراج العداد القياسي في بيئة واقعية عبر المستأجرين / داخل المتصفح ولم نتمكن من استعادة عدادات Edwards القياسية حتى مع 100,000 عينة توقيت.
استخدم لغات منخفضة المستوى بدلاً من JS / WASM إذا كان هدفك هو الأمان المطلق.
تستخدم المكتبة في الغالب Uint8Arrays و bigints.
.fill(0) التي تأمر بملء المحتوى بالأصفار
لكن لا توجد ضمانات في JSawait fn() ستكتب جميع المتغيرات الداخلية إلى الذاكرة. مع
الدوال غير المتزامنة لا توجد ضمانات متى سيتم تنفيذ
جزء الكود. مما يعني أن المهاجم قد يكون لديه
متسع من الوقت لقراءة البيانات من الذاكرة.هذا يعني أن بعض الأسرار قد تبقى في الذاكرة لفترة أطول من المتوقع. ومع ذلك، إذا كان المهاجم قادرًا على قراءة ذاكرة التطبيق، فهو محكوم عليه بالفشل على أي حال: لا توجد طريقة لضمان أي شيء بشأن تصفير البيانات الحساسة دون مجموعة اختبارات معقدة ستفرغ ذاكرة العملية وتتحقق من عدم بقاء أي بيانات حساسة. بالنسبة لـ JS فهذا يعني اختبار جميع المتصفحات (بما في ذلك المحمولة). وبالطبع، سيكون عديم الفائدة دون استخدام نفس مجموعة الاختبارات في التطبيق الفعلي الذي يستهلك المكتبة.
لهذه الحزمة، هناك اعتمادية واحدة؛ وعدد قليل من اعتماديات التطوير:
نعتمد على
crypto.getRandomValues المدمجة،
والتي تُعتبر PRNG آمنة تشفيريًا.
كانت لدى المتصفحات نقاط ضعف في الماضي - وقد تتكرر - لكن تنفيذ CSPRNG في مساحة المستخدم أسوأ، لأنه لا يوجد مصدر موثوق للعشوائية عالية الجودة في مساحة المستخدم.
الحاسوب الكمومي ذو الصلة التشفيرية، إذا تم بناؤه، سيتيح كسر تشفير المنحنى الإهليلجي (سواء ECDSA / EdDSA و ECDH) باستخدام خوارزمية Shor.
فكّر في التحول إلى خوارزميات أحدث / هجينة، مثل SPHINCS+. وهي متاحة في noble-post-quantum.
تحظر NIST التشفير الكلاسيكي (RSA، DSA، ECDSA، ECDH) بعد 2035. وتحظره ASD الأسترالية بعد 2030.
npm run benchmark
noble-curves تستغرق أكثر من 10 مللي ثانية لتوليد أكثر من 20 ميجابايت من الحسابات المسبقة للنقاط الأساسية.
يتم ذلك **مرة واحدة** لكل منحنى.
يتم تأجيل التوليد حتى يتم استدعاء أي دالة (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 للسلاسل فقط: استخدم Point.fromBytes مع Uint8Array{prehash: false}{lowS: false}{format: 'der'}.
يقلل ذلك من القابلية للتلاعبsignature.toBytes() أولاًlongSignatures (مفاتيح عامة G1، توقيعات G2) و shortSignatures (توقيعات G1، مفاتيح عامة G2){message: ..., publicKey: ...}[]إعادة التسمية (تُبرز curves v1.9 الأسماء القديمة كأسماء مهملة):
abstract/curve.jsPoint.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 سيبني الكود ويشغّل الاختبارات.
هناك مجموعات إضافية: اختبارات slow large-scalar / large-curve عبر npm run test:slow،
وأداة اختبار ثبات الزمن npm run benchmark:ct.
راجع paulmillr.com/noble للحصول على موارد ومقالات وتوثيق وعروض توضيحية مفيدة متعلقة بالمكتبة.
رخصة MIT (MIT)
حقوق النشر (c) 2022 Paul Miller (https://paulmillr.com)
راجع ملف LICENSE.
curvehash-to-curvemodularposeidonutilsweierstrass_shortw_utilssecp256k1weierstrass() + ecdsa() / edwards() + eddsa().
يتوقع weierstrass / edwards معاملات منحنى مبسّطة (أصبح Fp هو p)؛
يتوقع ecdsa / eddsa صنف Point ودالة التجزئة