
Implementazione JS minimale e verificata di crittografia a curva ellittica.
Implementazione JS minimale e verificata di crittografia a curve ellittiche.
Le curve hanno progetti gemelli da 5kb secp256k1 & ed25519. Hanno una superficie di attacco più piccola, ma meno funzionalità.
noble cryptography — set di librerie e strumenti crittografici ad alta sicurezza e facilmente verificabili.
npm install @noble/curves
deno add jsr:@noble/curves
Supportiamo tutte le principali piattaforme e runtime. Per React Native, potresti aver bisogno di un polyfill per getRandomValues. È disponibile anche un file standalone 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);
- [Firme ECDSA, EdDSA, Schnorr](#ecdsa-eddsa-schnorr-signatures)
- [ECDH: segreti condivisi Diffie-Hellman](#ecdh-diffie-hellman-shared-secrets)
- [webcrypto: wrapper semplificato](#webcrypto-friendly-wrapper)
- [Firme BLS, bls12-381, bn254 aka alt\_bn128](#bls-signatures-bls12-381-bn254-aka-alt_bn128)
- [hash-to-curve: hashing verso punti della curva](#hash-to-curve-hashing-to-curve-points)
- [OPRF](#oprfs) | [Firme soglia FROST](#frost-threshold-signatures)
- [poseidon: hash Poseidon](#poseidon-poseidon-hash) | [fft: Trasformata di Fourier veloce](#fft-fast-fourier-transform) | [utils](#utils-byte-shuffling-conversion)
- Interni: [Aritmetica dei punti](#elliptic-curve-point-math) | [modulare](#modular-modular-arithmetics--finite-fields) | [curve personalizzate](#weierstrass-custom-weierstrass-curve--ecdsa)
- [Specifiche](#specs)
- [Sicurezza](#security) | [Velocità](#speed) | [Aggiornamento](#upgrading) | [Contribuire e testare](#contributing--testing) | [Licenza](#license)
### Firme 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);
I messaggi vengono sempre sottoposti prima a hash: vedi firma pre-hashed. ECDSA utilizza k deterministico, EdDSA segue RFC 8032, Schnorr (solo secp256k1) segue BIP 340: vedi Specifiche.
Lo schema di firma MuSig2 e la mappatura BIP324 ElligatorSwift per secp256k1 sono disponibili in un pacchetto separato.
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);
Consulta [RFC 9496](https://www.rfc-editor.org/rfc/rfc9496) per maggiori informazioni su ristretto255 e decaf448.
Consulta la documentazione separata per [Point](#elliptic-curve-point-math), [hasher](#hash-to-curve-hashing-to-curve-points) e [oprf](#oprfs).
#### Firma pre-hashed```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 });
Per impostazione predefinita (prehash: true), sign() e verify() applicano prima l'hash integrato della curva al messaggio:
sha256 per secp256k1, sha512 per p521. prehash: false consente di utilizzare un hash personalizzato
(ad es. secp256k1 + keccak_256). In noble-curves v1, prehash: false era l'impostazione predefinita.
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');
Il recupero della chiave pubblica è supportato solo con ECDSA. È una semplice operazione matematica:
non ci sono garanzie che la firma sia stata effettivamente eseguita. Una tupla (r, s, h) falsificata viene recuperata in
una chiave pubblica casuale, ma non è fattibile trovare m che porterebbe a questo specifico h falsificato.
#### ECDSA con copertura tramite rumore```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 });
Per impostazione predefinita, le firme ECDSA sono deterministiche (RFC 6979). Le firme puramente deterministiche sono
vulnerabili agli attacchi di tipo fault, quindi schemi più recenti, come BIP340 schnorr, incorporano casualità
nella generazione della firma - ovvero hedging. extraEntropy abilita la modalità hedged. Per maggiori informazioni, consulta
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` (predefinito) utilizza le regole di verifica più permissive, [compatibili con il consenso](https://hdevalence.ca/blog/2020-10-04-its-25519am), definite in [ZIP215](https://zips.z.cash/zip-0215).
* `zip215: false` applica la verifica rigorosa RFC 8032 / FIPS 186-5 e aggiunge il
non-ripudio basato su SBS, utile per la firma di contratti, il voto elettronico e le blockchain.
Entrambe le modalità dispongono di SUF-CMA (strong unforgeability under chosen message attacks);
la maggior parte delle altre librerie non dispone né di SUF-CMA né di SBS.
Per maggiori informazioni, consultare [Taming the many EdDSAs](https://eprint.iacr.org/2020/1244).
### ECDH: segreti condivisi 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);
Forniamo ECDH su tutte le curve Weierstrass, e su 2 curve Montgomery X25519 (Curve25519) e X448 (Curve448), conformi a RFC 7748.
Nelle curve Weierstrass, i segreti condivisi:
key.slice(1) per rimuoverlisha256(shared) o 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 sottile wrapper su WebCrypto integrato, che rispecchia l'API noble. I metodi sono sempre asincroni;
il supporto a runtime varia - verifica con `await curve.isSupported()`.
Dai un'occhiata a [micro-key-producer](https://github.com/paulmillr/micro-key-producer) per
utility di conversione chiavi in puro JS.
### Firme BLS, bls12-381, bn254 aka 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
Per un esempio di utilizzo, consulta l'implementazione dei precompilati BLS EVM.
L'API BN254 rispecchia bls12-381. La curva era precedentemente chiamata alt_bn128. L'implementazione è compatibile con EIP-196 e EIP-197: consulta l'implementazione dei precompilati bn254 EVM. I punti bn254 non implementano toBytes, poiché non esiste uno standard di serializzazione: le implementazioni divergono su endianness, flag e ordine della parte immaginaria di G2. Inizializza i punti da 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';
Il modulo consente di eseguire l'hashing di stringhe arbitrarie su punti di curve ellittiche. Implementa [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
I namespace `_hasher` sono separati dalle curve per il tree-shaking:
gli utenti che non necessitano di hash-to-curve non lo avranno nelle loro build.
### OPRF```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';
Forniamo OPRF (oblivious pseudorandom functions), conformi a RFC 9497.
OPRF consente di creare in modo interattivo un Output = PRF(Input, serverSecretKey):
FROST implementa la firma Schnorr soglia di RFC 9591.
È simile al multisig dal punto di vista dell'applicazione: qualsiasi min di max partecipanti
può produrre congiuntamente una firma Schnorr sotto una chiave pubblica condivisa.
Le ciphersuite supportate sono p256_FROST, ed25519_FROST, ed448_FROST, ristretto255_FROST,
secp256k1_FROST, e schnorr_FROST (secp256k1 compatibile con Taproot).
La firma ha due round: i firmatari selezionati si impegnano prima, poi producono le quote di firma.```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 generazione delle chiavi può essere effettuata con un trusted dealer (sopra) o con DKG (distributed key generation).
DKG prevede tre round: i partecipanti si impegnano nella generazione delle chiavi, scambiano le quote private,
poi derivano le chiavi finali dei partecipanti - vedi l'utilizzo di `DKG.round1` / `round2` / `round3` in
[the tests](https://github.com/paulmillr/noble-curves/blob/main/test/rfc9591-frost.test.ts).
La libreria implementa i passaggi crittografici, non il protocollo applicativo circostante:
chi la utilizza deve comunque gestire comunicazione autenticata, coordinamento, retry, gestione delle sessioni e policy.
### poseidon: Poseidon hash
Implementa l'hash ZK-friendly [Poseidon](https://www.poseidon-hash.info):
permutazione e sponge.
Esistono molte varianti di poseidon con costanti diverse.
Non le forniamo: dovresti costruirle manualmente.
Dai un'occhiata al pacchetto [scure-starknet](https://github.com/paulmillr/scure-starknet) per un esempio adeguato.```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 (Fast Fourier Transform) su campi finiti.
### utils: byte shuffling, conversione```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 });
Ogni curva espone la sua classe Point: secp256k1, schnorr, p256, p384, p521, ed25519, ed448,
ristretto255, decaf448, bls12_381.G1 / G2, bn254.G1, jubjub, babyjubjub.
I punti Weierstrass usano coordinate proiettive (omogenee) `new Point(X, Y, Z)`,
i punti edwards usano coordinate estese `new Point(X, Y, Z, T)`; entrambi con x=X/Z, y=Y/Z.
#### modular: Aritmetica modulare e campi finiti```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
Tutte le operazioni aritmetiche sono eseguite con bigint JS su campi finiti,
definiti dal sottomodulo modular.
Le operazioni sui campi non sono a tempo costante: vedere security.
Il fatto è per lo più irrilevante, ma il metodo importante da tenere a mente è pow,
che può rivelare i bit dell'esponente, se usato ingenuamente.
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 formula della curva di Weierstrass in forma breve è `y² = x³ + ax + b`. `weierstrass`
si aspetta gli argomenti `a`, `b`, la caratteristica del campo `p`, l'ordine della curva `n`,
il cofattore `h` e le coordinate `Gx`, `Gy` del punto generatore, e restituisce una classe Point.
`ecdsa` combina una classe Point con una funzione di hash in uno schema di firma.
#### edwards: Curva di Edwards personalizzata```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 formula della curva di Twisted Edwards è ax² + y² = 1 + dx²y².
È necessario specificare a, d, la caratteristica del campo p, l'ordine della curva n (talvolta chiamato L),
il cofattore h e le coordinate Gx, Gy del punto generatore.
La libreria è stata sottoposta a audit:
curve, modular, poseidon, weierstrassAbbiamo avviato regolari self-audit assistiti dall'IA in Apr 2026.
È testata contro vettori property-based, cross-library e Wycheproof, ed è sottoposta a fuzzing nella CI di github.
Se noti qualcosa di insolito: indaga e segnala.
Puntiamo al tempo costante algoritmico. Il compilatore JIT e il Garbage Collector rendono estremamente difficile ottenere la resistenza ai timing attack con "tempo costante" in un linguaggio di scripting. Il che significa che qualsiasi altra libreria JS non può avere constant-timeness. Persino Rust, un linguaggio tipizzato staticamente senza GC, rende più difficile ottenere il tempo costante in alcuni casi. Se il tuo obiettivo è la sicurezza assoluta, non usare alcuna libreria JS — incluse le binding a librerie native. Usa librerie e linguaggi di basso livello.
Entro questi limiti, la moltiplicazione per scalare segreto fornisce proprietà specifiche e misurabili:
multiply() usa tabelle a finestra fissa con segno e
scansioni di tabella data-oblivious — il numero e l'ordine delle operazioni sui punti
sono indipendenti dal valore dello scalare.s + r·n con un r casuale
a 128 bit prima della moltiplicazione. Questo si applica a tutte le moltiplicazioni su curve
con cofattore 1 (p256, p384, p521, secp256k1), e alle moltiplicazioni per il punto base ovunque.benchmark/ct.ts)
confronta i tempi tra classi di scalari avversariali (sparsi vs densi, bit bassi vs alti,
vicini all'ordine, pattern di bit). La moltiplicazione per il punto base non mostra tempi distinguibili su nessuna
curva, e la moltiplicazione per punto casuale non ne mostra sulle curve Weierstrass
(max |t| ≤ 2.8 a 1000 campioni; soglia 4.5).Limitazione nota: sulle curve Edwards con cofattore (ed25519, ed448), moltiplicare un punto non-base per uno scalare segreto non è sottoposto a blinding. Lo stesso harness rileva questo in modo affidabile. La firma EdDSA non è interessata (moltiplica solo il punto base sottoposto a blinding), e X25519/X448 usano una separata implementazione a scala di Montgomery (anch'essa non interessata). È rilevante per i protocolli che moltiplicano punti Edwards/Ristretto arbitrari per scalari segreti di lunga durata; in quei casi preferisci scalari a larghezza piena per costruzione. Nota che la rilevabilità in un harness isolato non implica sfruttabilità pratica: abbiamo tentato l'estrazione dello scalare in un contesto realistico cross-tenant / in-browser e non siamo riusciti a recuperare gli scalari Edwards nemmeno con 100.000 campioni di timing.
Usa linguaggi di basso livello invece di JS / WASM se il tuo obiettivo è la sicurezza assoluta.
La libreria usa principalmente Uint8Array e bigint.
.fill(0) che indica di riempire il contenuto con zeri
ma non ci sono garanzie in JSawait fn() scriverà tutte le variabili interne in memoria. Con
le funzioni async non ci sono garanzie su quando il blocco di codice
verrebbe eseguito. Il che significa che un attaccante può avere
molto tempo per leggere i dati dalla memoria.Questo significa che alcuni segreti potrebbero rimanere in memoria più a lungo del previsto. Tuttavia, se un attaccante può leggere la memoria dell'applicazione, è comunque spacciato: non c'è modo di garantire nulla sull'azzeramento di dati sensibili senza una complessa suite di test che effettui il dump della memoria del processo e verifichi che non sia rimasto alcun dato sensibile. Per JS questo significa testare tutti i browser (inclusi quelli mobile). E, naturalmente, sarà inutile senza usare la stessa suite di test nell'applicazione reale che consuma la libreria.
Per questo pacchetto, c'è 1 dipendenza; e alcune dipendenze di sviluppo:
Ci affidiamo alla funzione integrata
crypto.getRandomValues,
che è considerata un PRNG crittograficamente sicuro.
I browser hanno avuto debolezze in passato - e potrebbero averne di nuovo - ma implementare un CSPRNG in userspace è persino peggio, poiché non esiste una fonte affidabile in userspace di entropia di alta qualità.
Un computer quantistico crittograficamente rilevante, se costruito, permetterà di violare la crittografia a curve ellittiche (sia ECDSA / EdDSA che ECDH) usando l'algoritmo di Shor.
Considera di passare ad algoritmi più recenti / ibridi, come SPHINCS+. Sono disponibili in noble-post-quantum.
NIST vieta la crittografia classica (RSA, DSA, ECDSA, ECDH) dopo il 2035. L'ASD australiano la vieta dopo il 2030.
npm run benchmark
noble-curves impiega più di 10 ms per generare oltre 20 MB di precalcoli del punto base.
Questa operazione viene eseguita **una sola volta** per curva.
La generazione viene posticipata fino a quando non viene chiamato un metodo qualsiasi (pubkey, sign, verify).
L'utente può forzare la generazione dei precalcoli chiamando manualmente `Point.BASE.precompute(windowSize, false)`.
Dai un'occhiata al codice sorgente.
Risultati del benchmark su 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
Versioni di node.js supportate:
v2 semplifica enormemente gli interni, migliora la sicurezza, riduce la dimensione del bundle e apre la strada per il futuro. Abbiamo cercato di mantenere v2 il più possibile retrocompatibile.
Percorso di aggiornamento: aggiorna prima a curves v1.9.x. Correggi gli avvisi di deprecazione, poi passa a v2.
Moduli:
.js ora è obbligatoria: @noble/curves/ed25519 => @noble/curves/ed25519.js.
Questo abilita l'uso nativo nel browser, senza transpilerp256, p384, p521 sono stati spostati in nist; jubjub è stato spostato in miscpasta e bn254_weierstrass (NON bn254 basata su pairing) sono state rimosseNuove funzionalità:
isValidSecretKey, isValidPublicKeyModifiche incompatibili:
Point.fromHex ora è solo per stringhe: usa Point.fromBytes per Uint8Array{prehash: false}{lowS: false}{format: 'der'}.
Questo riduce la malleabilitàsignature.toBytes()longSignatures (pubkey G1, firme G2) e shortSignatures (firme G1, pubkey G2){message: ..., publicKey: ...}[]Rinomine (curves v1.9 evidenzia i vecchi nomi come deprecati):
abstract/curve.jsPoint.BASE.multiply(Point.Fn.fromBytes(key))CURVE con ogni tipo di roba casuale => Point.CURVE(),
che fornisce solo i parametri della curva*curve*_hasher.
Esempio: secp256k1.hashToCurve => secp256k1_hasher.hashToCurve()Funzionalità rimosse: Point#multiplyAndAddUnsafe, Point#hasEvenY, Field.MASK
npm install && npm run build && npm test compilerà il codice ed eseguirà i test.
Ci sono suite aggiuntive: test lenti con scalari grandi / curve grandi npm run test:slow,
e l'harness per la constant-timeness npm run benchmark:ct.
Vedi paulmillr.com/noble per risorse utili, articoli, documentazione e demo relative alla libreria.
The MIT License (MIT)
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
Vedi il file LICENSE.
curve, hash-to-curve, modular, poseidon, utils, weierstrass e
moduli di primo livello _shortw_utils e secp256k1weierstrass() + ecdsa() / edwards() + eddsa().
weierstrass / edwards si aspettano parametri di curva semplificati (Fp è diventato p);
ecdsa / eddsa si aspettano la classe Point e l'hash