
Implémentation JS auditée et minimale de cryptographie sur courbes elliptiques.
Implémentation JS auditée et minimale de cryptographie sur courbes elliptiques.
Les courbes ont des projets frères de 5 Ko secp256k1 & ed25519. Ils ont une surface d'attaque plus réduite, mais moins de fonctionnalités.
noble cryptography — ensemble de bibliothèques et d'outils cryptographiques à haute sécurité, facilement auditables et contenus.
npm install @noble/curves
deno add jsr:@noble/curves
Nous prenons en charge toutes les principales plateformes et environnements d'exécution. Pour React Native, vous pourriez avoir besoin d'un polyfill pour getRandomValues. Un fichier autonome noble-curves.js est également disponible.```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);
- [Signatures ECDSA, EdDSA, Schnorr](#ecdsa-eddsa-schnorr-signatures)
- [ECDH : secrets partagés Diffie-Hellman](#ecdh-diffie-hellman-shared-secrets)
- [webcrypto : wrapper convivial](#webcrypto-friendly-wrapper)
- [Signatures BLS, bls12-381, bn254 alias alt\_bn128](#bls-signatures-bls12-381-bn254-aka-alt_bn128)
- [hash-to-curve : hachage vers des points de courbe](#hash-to-curve-hashing-to-curve-points)
- [OPRFs](#oprfs) | [Signatures de seuil FROST](#frost-threshold-signatures)
- [poseidon : hash Poseidon](#poseidon-poseidon-hash) | [fft : Transformée de Fourier rapide](#fft-fast-fourier-transform) | [utils](#utils-byte-shuffling-conversion)
- Internes : [Mathématiques de points](#elliptic-curve-point-math) | [modulaire](#modular-modular-arithmetics--finite-fields) | [courbes personnalisées](#weierstrass-custom-weierstrass-curve--ecdsa)
- [Spécifications](#specs)
- [Sécurité](#security) | [Vitesse](#speed) | [Mise à niveau](#upgrading) | [Contribution et tests](#contributing--testing) | [Licence](#license)
### Signatures 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);
Les messages sont toujours hachés d'abord : voir signature pré-hachée. ECDSA utilise un k déterministe, EdDSA suit la RFC 8032, Schnorr (secp256k1 uniquement) suit le BIP 340 : voir Spécifications.
Le schéma de signature MuSig2 et le mapping ElligatorSwift BIP324 pour secp256k1 sont disponibles dans un package séparé.
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);
Consultez la [RFC 9496](https://www.rfc-editor.org/rfc/rfc9496) pour plus d'informations sur ristretto255 et decaf448.
Consultez la documentation séparée pour [Point](#elliptic-curve-point-math), [hasher](#hash-to-curve-hashing-to-curve-points) et [oprf](#oprfs).
#### Signature pré-hachée```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 });
Par défaut (prehash: true), sign() et verify() appliquent d'abord le hachage intégré de la courbe au message :
sha256 pour secp256k1, sha512 pour p521. prehash: false permet d'utiliser un hachage personnalisé
(par exemple secp256k1 + keccak_256). Dans noble-curves v1, prehash: false était la valeur par défaut.
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');
La récupération de clé publique n'est prise en charge qu'avec ECDSA. Il s'agit d'une simple opération mathématique :
aucune garantie n'existe que la signature a réellement été effectuée. Un triplet (r, s, h) falsifié donne une clé publique aléatoire, mais il n'est pas réalisable de trouver m qui conduirait à ce h falsifié spécifique.
#### ECDSA couvert avec bruit```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 });
Par défaut, les signatures ECDSA sont déterministes (RFC 6979). Les signatures purement déterministes sont
vulnérables aux attaques par faute, c'est pourquoi les schémas plus récents, tels que BIP340 schnorr, intègrent de l'aléatoire
dans la génération de signature - aussi appelé hedging. extraEntropy active le mode hedged. Pour plus d'informations, consultez
Deterministic signatures are not your friends.
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` (par défaut) utilise les règles de vérification plus permissives et [compatibles avec le consensus](https://hdevalence.ca/blog/2020-10-04-its-25519am) définies dans [ZIP215](https://zips.z.cash/zip-0215).
* `zip215: false` applique une vérification stricte RFC 8032 / FIPS 186-5 et ajoute la
non-répudiation basée sur SBS, ce qui est utile pour la signature de contrats, le vote électronique et les blockchains.
Les deux modes disposent de SUF-CMA (forte infalsifiabilité sous attaques à messages choisis) ;
la plupart des autres bibliothèques n'ont ni SUF-CMA ni SBS.
Voir [Taming the many EdDSAs](https://eprint.iacr.org/2020/1244) pour plus d'informations.
### ECDH : secrets partagés 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);
Nous fournissons ECDH sur toutes les courbes de Weierstrass, et sur 2 courbes de Montgomery X25519 (Curve25519) & X448 (Curve448), conformément à la RFC 7748.
Dans les courbes de Weierstrass, les secrets partagés :
key.slice(1) pour les retirersha256(shared) ou 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');
Un mince wrapper au-dessus de WebCrypto intégré, reflétant l'API noble. Les méthodes sont toujours asynchrones ;
la prise en charge à l'exécution varie - vérifiez avec `await curve.isSupported()`.
Consultez [micro-key-producer](https://github.com/paulmillr/micro-key-producer) pour
des utilitaires de conversion de clés en pur JS.
### Signatures BLS, bls12-381, bn254 alias 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
Pour un exemple d'utilisation, consultez l'implémentation des précompilations BLS EVM.
L'API BN254 reflète celle de bls12-381. La courbe était auparavant appelée alt_bn128. L'implémentation est compatible avec EIP-196 et EIP-197 : consultez l'implémentation des précompilations bn254 EVM. Les points bn254 n'implémentent pas toBytes, car il n'existe pas de standard de sérialisation : les implémentations divergent sur l'endianness, les flags et l'ordre de la partie imaginaire de G2. Initialisez les points à partir de bigints à la place.
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';
Le module permet de hacher des chaînes arbitraires en points de courbe elliptique. Implémente la [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
Les espaces de noms `_hasher` sont séparés des courbes pour le tree-shaking :
les utilisateurs qui n'ont pas besoin du hash-to-curve ne l'auront pas dans leurs builds.
### 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';
Nous fournissons des OPRF (fonctions pseudorandom oblivious), conformes à la RFC 9497.
OPRF permet de créer de manière interactive un Output = PRF(Input, serverSecretKey) :
FROST implémente la signature Schnorr à seuil RFC 9591.
Du point de vue de l'application, cela est similaire au multisig : n'importe quel min de max participants
peut produire conjointement une signature Schnorr sous une clé publique partagée.
Les suites cryptographiques prises en charge sont p256_FROST, ed25519_FROST, ed448_FROST, ristretto255_FROST,
secp256k1_FROST, et schnorr_FROST (secp256k1 compatible Taproot).
La signature comporte deux tours : les signataires sélectionnés s'engagent d'abord, puis produisent les parts de signature.```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]);
La génération de clés peut être effectuée avec un trusted dealer (ci-dessus) ou avec DKG (distributed key generation).
DKG comporte trois tours : les participants s'engagent sur la génération de clés, échangent des parts privées,
puis dérivent les clés finales des participants - voir l'utilisation de `DKG.round1` / `round2` / `round3` dans
[les tests](https://github.com/paulmillr/noble-curves/blob/main/test/rfc9591-frost.test.ts).
La bibliothèque implémente les étapes cryptographiques, pas le protocole applicatif environnant :
les appelants doivent toujours gérer la communication authentifiée, la coordination, les tentatives, la gestion de session et la politique.
### poseidon: Poseidon hash
Implémente le hash ZK-friendly [Poseidon](https://www.poseidon-hash.info) :
permutation et sponge.
Il existe de nombreuses variantes de poseidon avec des constantes différentes.
Nous ne les fournissons pas : vous devez les construire manuellement.
Consultez le paquet [scure-starknet](https://github.com/paulmillr/scure-starknet) pour un exemple approprié.```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 (Transformée de Fourier rapide) sur des corps finis.
### utils : réorganisation d'octets, conversion```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 });
Chaque courbe expose sa classe Point : secp256k1, schnorr, p256, p384, p521, ed25519, ed448,
ristretto255, decaf448, bls12_381.G1 / G2, bn254.G1, jubjub, babyjubjub.
Les points Weierstrass utilisent des coordonnées projectives (homogènes) `new Point(X, Y, Z)`,
les points edwards utilisent des coordonnées étendues `new Point(X, Y, Z, T)` ; les deux avec x=X/Z, y=Y/Z.
#### modular : arithmétique modulaire et corps finis```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
Toute l'arithmétique est effectuée avec des bigints JS sur des corps finis,
qui sont définis à partir du sous-module modular.
Les opérations sur les corps ne sont pas à temps constant : voir sécurité.
Ce fait est généralement sans importance, mais la méthode importante à garder à l'esprit est pow,
qui peut divulguer des bits de l'exposant lorsqu'elle est utilisée naïvement.
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);
La formule de la courbe de Weierstrass courte est `y² = x³ + ax + b`. `weierstrass`
attend les arguments `a`, `b`, la caractéristique du corps `p`, l'ordre de la courbe `n`,
le cofacteur `h` et les coordonnées `Gx`, `Gy` du point générateur, et renvoie une classe Point.
`ecdsa` combine une classe Point avec une fonction de hachage en un schéma de signature.
#### edwards : Courbe d'Edwards personnalisée```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);
La formule de la courbe de Twisted Edwards est ax² + y² = 1 + dx²y².
Vous devez spécifier a, d, la caractéristique du corps p, l'ordre de la courbe n (parfois nommé L),
le cofacteur h et les coordonnées Gx, Gy du point générateur.
La bibliothèque a été auditée :
curve, modular, poseidon, weierstrassNous avons commencé des auto-audits réguliers assistés par IA en avril 2026.
Elle est testée contre des vecteurs basés sur les propriétés, inter-bibliothèques et Wycheproof, et est soumise au fuzzing dans la CI GitHub.
Si vous voyez quelque chose d'inhabituel : enquêtez et signalez-le.
Nous visons un temps algorithmique constant. Le compilateur JIT et le ramasse-miettes rendent la résistance aux attaques temporelles extrêmement difficile à atteindre dans un langage de script. Ce qui signifie que toute autre bibliothèque JS ne peut pas avoir de constance temporelle. Même Rust, un langage à typage statique sans GC, rend plus difficile l'obtention d'un temps constant dans certains cas. Si votre objectif est une sécurité absolue, n'utilisez aucune bibliothèque JS — y compris les bindings vers des bibliothèques natives. Utilisez des bibliothèques et des langages de bas niveau.
Dans ces limites, la multiplication par un scalaire secret fournit des propriétés spécifiques et mesurables :
multiply() utilise des tables à fenêtre fixe signée avec
des parcours de table indépendants des données — le nombre et l'ordre des opérations sur les points
sont indépendants de la valeur du scalaire.s + r·n avec un r aléatoire
de 128 bits avant la multiplication. Cela s'applique à toutes les multiplications sur les courbes
de cofacteur 1 (p256, p384, p521, secp256k1), et aux multiplications par le point de base partout.benchmark/ct.ts)
compare les temps d'exécution entre des classes de scalaires adverses (creux vs denses, bits faibles vs élevés,
proches de l'ordre, motifs binaires). La multiplication par le point de base ne montre aucun temps distinguable sur aucune
courbe, et la multiplication par un point aléatoire n'en montre aucun sur les courbes de Weierstrass
(max |t| ≤ 2,8 à 1000 échantillons ; seuil 4,5).Limitation connue : sur les courbes d'Edwards à cofacteur (ed25519, ed448), la multiplication d'un point non-base par un scalaire secret n'est pas aveuglée. Le même harnais détecte cela de manière fiable. La signature EdDSA n'est pas affectée (elle ne multiplie que le point de base aveuglé), et X25519/X448 utilisent une implémentation séparée d'échelle de Montgomery (également non affectée). Cela importe pour les protocoles qui multiplient des points Edwards/Ristretto arbitraires par des scalaires secrets de longue durée ; préférez-y des scalaires de pleine largeur par construction. Notez que la détectabilité dans un harnais isolé n'implique pas une exploitabilité pratique : nous avons tenté une extraction de scalaire dans un cadre réaliste multi-locataires / en navigateur et n'avons pas pu récupérer les scalaires Edwards même avec 100 000 échantillons temporels.
Utilisez des langages de bas niveau plutôt que JS / WASM si votre objectif est une sécurité absolue.
La bibliothèque utilise principalement des Uint8Arrays et des bigints.
.fill(0) qui demande de remplir le contenu avec des zéros
mais il n'y a aucune garantie en JSawait fn() écrira toutes les variables internes en mémoire. Avec
les fonctions asynchrones, il n'y a aucune garantie quant au moment où le bloc de code
serait exécuté. Ce qui signifie qu'un attaquant peut avoir
amplement le temps de lire les données en mémoire.Cela signifie que certains secrets pourraient rester en mémoire plus longtemps que prévu. Cependant, si un attaquant peut lire la mémoire de l'application, tout est perdu de toute façon : il n'y a aucun moyen de garantir quoi que ce soit concernant l'effacement des données sensibles sans une suite de tests complexe qui videra la mémoire du processus et vérifiera qu'il ne reste aucune donnée sensible. Pour JS, cela signifie tester tous les navigateurs (y compris mobiles). Et, bien sûr, cela sera inutile sans utiliser la même suite de tests dans l'application réelle qui consomme la bibliothèque.
Pour ce paquet, il y a 1 dépendance ; et quelques dépendances de développement :
Nous nous appuyons sur la fonction intégrée
crypto.getRandomValues,
qui est considérée comme un PRNG cryptographiquement sûr.
Les navigateurs ont eu des faiblesses par le passé — et pourraient en avoir à nouveau — mais implémenter un CSPRNG en espace utilisateur est encore pire, car il n'existe pas de source fiable d'entropie de haute qualité en espace utilisateur.
Un ordinateur quantique cryptographiquement pertinent, s'il est construit, permettra de casser la cryptographie sur courbes elliptiques (à la fois ECDSA / EdDSA et ECDH) en utilisant l'algorithme de Shor.
Envisagez de passer à des algorithmes plus récents / hybrides, tels que SPHINCS+. Ils sont disponibles dans noble-post-quantum.
Le NIST interdit la cryptographie classique (RSA, DSA, ECDSA, ECDH) après 2035. L'ASD australien l'interdit après 2030.
npm run benchmark
noble-curves passe plus de 10 ms à générer plus de 20 Mo de précalculs de points de base.
Cela est effectué **une seule fois** par courbe.
La génération est différée jusqu'à ce qu'une méthode (pubkey, sign, verify) soit appelée.
L'utilisateur peut forcer la génération des précalculs en appelant manuellement `Point.BASE.precompute(windowSize, false)`.
Consultez le code source.
Résultats de benchmark sur 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
Versions de node.js prises en charge :
v2 simplifie massivement les internes, améliore la sécurité, réduit la taille du bundle et prépare l'avenir. Nous avons essayé de conserver v2 aussi rétrocompatible que possible.
Chemin de mise à niveau : mettez d'abord à niveau vers curves v1.9.x. Corrigez les avertissements de dépréciation, puis passez à v2.
Modules :
.js est désormais requise : @noble/curves/ed25519 => @noble/curves/ed25519.js.
Cela permet une utilisation native dans le navigateur, sans transpileursp256, p384, p521 ont été déplacés dans nist ; jubjub a été déplacé dans miscpasta et bn254_weierstrass (bn254 NON basé sur le pairing) ont été suppriméesNouvelles fonctionnalités :
isValidSecretKey, isValidPublicKeyChangements incompatibles :
Point.fromHex est désormais réservé aux chaînes : utilisez Point.fromBytes pour Uint8Array{prehash: false}{lowS: false}{format: 'der'}.
Cela réduit la malléabilitésignature.toBytes()longSignatures (clés publiques G1, signatures G2) et shortSignatures (signatures G1, clés publiques G2){message: ..., publicKey: ...}[]Renommages (curves v1.9 met en évidence les anciens noms comme dépréciés) :
abstract/curve.jsPoint.BASE.multiply(Point.Fn.fromBytes(key))CURVE avec toutes sortes de choses aléatoires => Point.CURVE(),
qui ne fournit que les paramètres de courbe*curve*_hasher.
Exemple : secp256k1.hashToCurve => secp256k1_hasher.hashToCurve()Fonctionnalités supprimées : Point#multiplyAndAddUnsafe, Point#hasEvenY, Field.MASK
npm install && npm run build && npm test compilera le code et exécutera les tests.
Il existe des suites supplémentaires : tests lents à grand scalaire / grande courbe npm run test:slow,
et le harnais de constance temporelle npm run benchmark:ct.
Voir paulmillr.com/noble pour des ressources utiles, des articles, de la documentation et des démos liés à la bibliothèque.
The MIT License (MIT)
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
Voir le fichier LICENSE.
curve, hash-to-curve, modular, poseidon, utils, weierstrass et
modules de haut niveau _shortw_utils et secp256k1weierstrass() + ecdsa() / edwards() + eddsa().
weierstrass / edwards attendent des paramètres de courbe simplifiés (Fp est devenu p) ;
ecdsa / eddsa attendent la classe Point et le hash