
Implementação JS auditada e minimalista de criptografia de curva elíptica.
Implementação JS auditada e minimalista de criptografia de curva elíptica.
As curvas têm projetos irmãos de 5kb secp256k1 & ed25519. Eles têm uma superfície de ataque menor, mas menos funcionalidades.
noble cryptography — conjunto de bibliotecas e ferramentas criptográficas contidas, de alta segurança e facilmente auditáveis.
npm install @noble/curves
deno add jsr:@noble/curves
Suportamos todas as principais plataformas e runtimes. Para React Native, você pode precisar de um polyfill para getRandomValues. Um arquivo autônomo noble-curves.js também está disponível.```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);
- [Assinaturas ECDSA, EdDSA, Schnorr](#ecdsa-eddsa-schnorr-signatures)
- [ECDH: segredos compartilhados Diffie-Hellman](#ecdh-diffie-hellman-shared-secrets)
- [webcrypto: wrapper amigável](#webcrypto-friendly-wrapper)
- [Assinaturas BLS, bls12-381, bn254 aka alt\_bn128](#bls-signatures-bls12-381-bn254-aka-alt_bn128)
- [hash-to-curve: hashing para pontos de curva](#hash-to-curve-hashing-to-curve-points)
- [OPRFs](#oprfs) | [Assinaturas de limiar FROST](#frost-threshold-signatures)
- [poseidon: hash Poseidon](#poseidon-poseidon-hash) | [fft: Transformada Rápida de Fourier](#fft-fast-fourier-transform) | [utils](#utils-byte-shuffling-conversion)
- Internos: [Aritmética de pontos](#elliptic-curve-point-math) | [modular](#modular-modular-arithmetics--finite-fields) | [curvas personalizadas](#weierstrass-custom-weierstrass-curve--ecdsa)
- [Especificações](#specs)
- [Segurança](#security) | [Velocidade](#speed) | [Atualização](#upgrading) | [Contribuição e testes](#contributing--testing) | [Licença](#license)
### Assinaturas 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);
As mensagens são sempre primeiro submetidas a hash: consulte assinatura pré-hasheada. O ECDSA usa k determinístico, o EdDSA segue o RFC 8032, o Schnorr (apenas secp256k1) segue o BIP 340: consulte Especificações.
O esquema de assinatura MuSig2 e o mapeamento BIP324 ElligatorSwift para secp256k1 estão disponíveis num pacote separado.
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);
Confira o [RFC 9496](https://www.rfc-editor.org/rfc/rfc9496) para mais informações sobre ristretto255 e decaf448.
Confira a documentação separada para [Point](#elliptic-curve-point-math), [hasher](#hash-to-curve-hashing-to-curve-points) e [oprf](#oprfs).
#### Assinatura pré-hasheada```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 });
Por padrão (prehash: true), sign() e verify() aplicam primeiro o hash integrado da curva à mensagem:
sha256 para secp256k1, sha512 para p521. prehash: false permite usar um hash personalizado
(por exemplo, secp256k1 + keccak_256). No noble-curves v1, prehash: false era o padrão.
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');
A recuperação de chave pública só é suportada com ECDSA. É uma operação matemática simples:
não há garantias de que a assinatura foi realmente feita. Um (r, s, h) forjado é recuperado em
uma chave pública aleatória, mas não é viável encontrar m que levaria a este h forjado específico.
#### ECDSA com hedge e ruído```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 });
Por padrão, as assinaturas ECDSA são determinísticas (RFC 6979). Assinaturas puramente determinísticas são
vulneráveis a ataques de falha, então esquemas mais recentes, como o schnorr BIP340, incorporam aleatoriedade
na geração de assinaturas - também conhecido como hedging. extraEntropy habilita o modo hedged. Para mais informações, confira
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` (padrão) usa as regras de verificação mais permissivas e [compatíveis com consenso](https://hdevalence.ca/blog/2020-10-04-its-25519am) definidas na [ZIP215](https://zips.z.cash/zip-0215).
* `zip215: false` impõe verificação estrita RFC 8032 / FIPS 186-5 e adiciona
não-repúdio baseado em SBS, o que é útil para assinatura de contratos, voto eletrónico e blockchains.
Ambos os modos têm SUF-CMA (strong unforgeability under chosen message attacks);
a maioria das outras bibliotecas não tem nem SUF-CMA nem SBS.
Consulte [Taming the many EdDSAs](https://eprint.iacr.org/2020/1244) para mais informações.
### ECDH: segredos partilhados 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);
Fornecemos ECDH sobre todas as curvas Weierstrass, e sobre 2 curvas Montgomery X25519 (Curve25519) & X448 (Curve448), em conformidade com a RFC 7748.
Em curvas Weierstrass, os segredos compartilhados:
key.slice(1) para removê-lossha256(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');
Um wrapper fino sobre o WebCrypto integrado, espelhando a API noble. Os métodos são sempre assíncronos;
o suporte em tempo de execução varia - verifique com `await curve.isSupported()`.
Confira [micro-key-producer](https://github.com/paulmillr/micro-key-producer) para
utilitários de conversão de chaves em JS puro.
### Assinaturas 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
Para exemplo de uso, confira a implementação dos precompilados BLS EVM.
A API BN254 espelha a bls12-381. A curva era anteriormente chamada alt_bn128. A implementação é compatível com EIP-196 e EIP-197: confira a implementação dos precompilados bn254 EVM. Os pontos bn254 não implementam toBytes, porque não há um padrão de serialização: as implementações divergem quanto ao endianness, flags e ordem da parte imaginária de G2. Inicialize pontos a partir de 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';
O módulo permite fazer hash de strings arbitrárias para pontos de curva elíptica. Implementa a [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380).
Os namespaces `_hasher` são separados das curvas para tree-shaking:
utilizadores que não precisam de hash-to-curve não o terão nas suas 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';
Fornecemos OPRFs (funções pseudorrandômicas oblivious), em conformidade com a RFC 9497.
OPRF permite criar interativamente um Output = PRF(Input, serverSecretKey):
FROST implementa a assinatura Schnorr de limiar RFC 9591.
É semelhante ao multisig do ponto de vista da aplicação: qualquer min de max participantes
podem produzir conjuntamente uma assinatura Schnorr sob uma chave pública partilhada.
Os ciphersuites suportados são p256_FROST, ed25519_FROST, ed448_FROST, ristretto255_FROST,
secp256k1_FROST, e schnorr_FROST (secp256k1 compatível com Taproot).
A assinatura tem duas rondas: os signatários selecionados comprometem-se primeiro, depois produzem as partilhas da assinatura.```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]);
A geração de chaves pode ser feita com um trusted dealer (acima) ou com DKG (geração de chaves distribuída).
O DKG tem três rodadas: os participantes se comprometem com a geração de chaves, trocam shares privados,
e então derivam as chaves finais dos participantes - veja o uso de `DKG.round1` / `round2` / `round3` em
[the tests](https://github.com/paulmillr/noble-curves/blob/main/test/rfc9591-frost.test.ts).
A biblioteca implementa os passos criptográficos, não o protocolo de aplicação ao redor:
os chamadores ainda precisam de comunicação autenticada, coordenação, retries, gerenciamento de sessão e política.
### poseidon: Poseidon hash
Implementa o hash ZK-friendly [Poseidon](https://www.poseidon-hash.info):
permutação e sponge.
Existem muitas variantes de poseidon com constantes diferentes.
Nós não as fornecemos: você deve construí-las manualmente.
Confira o pacote [scure-starknet](https://github.com/paulmillr/scure-starknet) para um exemplo adequado.```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 (Transformada Rápida de Fourier) sobre corpos finitos.
### utils: embaralhamento de bytes, conversão```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 });
Cada curva expõe sua classe Point: secp256k1, schnorr, p256, p384, p521, ed25519, ed448,
ristretto255, decaf448, bls12_381.G1 / G2, bn254.G1, jubjub, babyjubjub.
Pontos Weierstrass usam coordenadas projetivas (homogêneas) `new Point(X, Y, Z)`,
pontos edwards usam coordenadas estendidas `new Point(X, Y, Z, T)`; ambos com x=X/Z, y=Y/Z.
#### modular: Aritmética modular e corpos finitos```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
Toda a aritmética é feita com bigints de JS sobre corpos finitos,
que é definido a partir do submódulo modular.
As operações de corpo não são de tempo constante: consulte security.
O facto é maioritariamente irrelevante, mas o método importante a ter em mente é pow,
que pode revelar bits do expoente, quando usado de forma ingénua.
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);
A fórmula da curva de Weierstrass curta é `y² = x³ + ax + b`. `weierstrass`
espera os argumentos `a`, `b`, característica do corpo `p`, ordem da curva `n`,
cofator `h` e coordenadas `Gx`, `Gy` do ponto gerador, e retorna uma classe Point.
`ecdsa` combina uma classe Point com uma função de hash em um esquema de assinatura.
#### edwards: Curva de Edwards personalizada```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);
A fórmula da curva de Twisted Edwards é ax² + y² = 1 + dx²y².
Você deve especificar a, d, a característica do corpo p, a ordem da curva n (às vezes chamada de L),
o cofator h e as coordenadas Gx, Gy do ponto gerador.
A biblioteca foi auditada:
curve, modular, poseidon, weierstrassIniciamos autoauditorias regulares assistidas por IA em Abr 2026.
A biblioteca é testada contra vetores baseados em propriedades, entre bibliotecas e Wycheproof, e está sendo submetida a fuzzing no CI do GitHub.
Se você vir algo incomum: investigue e reporte.
Estamos visando tempo constante algorítmico. O compilador JIT e o Garbage Collector tornam extremamente difícil alcançar resistência a ataques de temporização em uma linguagem de script. O que significa que qualquer outra biblioteca JS não pode ter tempo constante. Mesmo Rust, uma linguagem com tipagem estática e sem GC, torna mais difícil alcançar tempo constante em alguns casos. Se seu objetivo é segurança absoluta, não use nenhuma biblioteca JS — incluindo bindings para bibliotecas nativas. Use bibliotecas e linguagens de baixo nível.
Dentro desses limites, a multiplicação por escalar secreto fornece propriedades específicas e mensuráveis:
multiply() usa tabelas de janela fixa com sinal e
varreduras de tabela que não dependem dos dados — o número e a ordem das operações de ponto
são independentes do valor do escalar.s + r·n com um r aleatório
de 128 bits antes da multiplicação. Isso se aplica a todas as multiplicações em curvas de cofator 1
(p256, p384, p521, secp256k1), e a multiplicações de ponto base em todos os lugares.benchmark/ct.ts)
compara o tempo entre classes de escalares adversariais (esparsos vs densos, bits baixos vs altos,
próximos da ordem, padrões de bits). A multiplicação de ponto base não mostra tempo distinguível em nenhuma
curva, e a multiplicação de ponto aleatório não mostra nenhum nas curvas de Weierstrass
(máx |t| ≤ 2,8 com 1000 amostras; limiar 4,5).Limitação conhecida: em curvas de Edwards com cofator (ed25519, ed448), multiplicar um ponto não-base por um escalar secreto não é protegido por blinding. O mesmo harness detecta isso de forma confiável. A assinatura EdDSA não é afetada (ela só multiplica o ponto base com blinding), e X25519/X448 usam uma implementação separada de escada de Montgomery (também não afetada). Isso importa para protocolos que multiplicam pontos arbitrários de Edwards/Ristretto por escalares secretos de longa duração; prefira escalares que sejam de largura total por construção nesses casos. Note que a detectabilidade em um harness isolado não implica explorabilidade prática: tentamos extração de escalar em um cenário realista entre inquilinos / no navegador e não conseguimos recuperar escalares de Edwards mesmo com 100.000 amostras de temporização.
Use linguagens de baixo nível em vez de JS / WASM se seu objetivo é segurança absoluta.
A biblioteca usa principalmente Uint8Arrays e bigints.
.fill(0) que instrui a preencher o conteúdo com zeros
mas não há garantias em JSawait fn() escreverá todas as variáveis internas na memória. Com
funções assíncronas não há garantias de quando o bloco de código
seria executado. O que significa que o atacante pode ter
bastante tempo para ler dados da memória.Isso significa que alguns segredos podem permanecer na memória por mais tempo do que o previsto. No entanto, se um atacante pode ler a memória da aplicação, já está tudo perdido: não há como garantir qualquer coisa sobre zerar dados sensíveis sem uma suíte de testes complexa que fará dump da memória do processo e verificará que não há dados sensíveis restantes. Para JS isso significa testar todos os navegadores (incluindo mobile). E, é claro, será inútil sem usar a mesma suíte de testes na aplicação real que consome a biblioteca.
Para este pacote, há 1 dependência; e algumas dependências de desenvolvimento:
Contamos com o
crypto.getRandomValues integrado,
que é considerado um PRNG criptograficamente seguro.
Navegadores tiveram fraquezas no passado — e podem ter novamente — mas implementar um CSPRNG em espaço de usuário é ainda pior, pois não há fonte confiável de entropia de alta qualidade em espaço de usuário.
Um computador quântico criptograficamente relevante, se construído, permitirá quebrar a criptografia de curva elíptica (tanto ECDSA / EdDSA quanto ECDH) usando o algoritmo de Shor.
Considere mudar para algoritmos mais novos / híbridos, como SPHINCS+. Eles estão disponíveis em noble-post-quantum.
O NIST proíbe criptografia clássica (RSA, DSA, ECDSA, ECDH) após 2035. O ASD australiano proíbe após 2030.
npm run benchmark
noble-curves gasta mais de 10 ms para gerar mais de 20 MB de pré-cálculos de pontos base.
Isto é feito **uma única vez** por curva.
A geração é adiada até que qualquer método (pubkey, sign, verify) seja chamado.
O utilizador pode forçar a geração de pré-cálculos chamando manualmente `Point.BASE.precompute(windowSize, false)`.
Consulte o código-fonte.
Resultados de benchmark em 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
Versões do node.js suportadas:
A v2 simplifica massivamente os internos, melhora a segurança, reduz o tamanho do bundle e prepara o caminho para o futuro. Tentámos manter a v2 o mais retrocompatível possível.
Caminho de atualização: atualize primeiro para curves v1.9.x. Corrija os avisos de depreciação e depois mude para a v2.
Módulos:
.js é agora obrigatória: @noble/curves/ed25519 => @noble/curves/ed25519.js.
Isto permite a utilização nativa no navegador, sem transpiladoresp256, p384, p521 foram movidos para nist; jubjub foi movido para miscpasta e bn254_weierstrass (NÃO bn254 baseada em emparelhamento) foram removidasNovas funcionalidades:
isValidSecretKey, isValidPublicKeyAlterações disruptivas:
Point.fromHex é agora apenas para strings: use Point.fromBytes para Uint8Array{prehash: false}{lowS: false}{format: 'der'}.
Isto reduz a maleabilidadesignature.toBytes() primeirolongSignatures (pubkeys G1, sigs G2) e shortSignatures (sigs G1, pubkeys G2){message: ..., publicKey: ...}[]Renomeações (curves v1.9 destaca os nomes antigos como depreciados):
abstract/curve.jsPoint.BASE.multiply(Point.Fn.fromBytes(key))CURVE com todo o tipo de coisas aleatórias => Point.CURVE(),
que apenas fornece parâmetros de curva*curve*_hasher.
Exemplo: secp256k1.hashToCurve => secp256k1_hasher.hashToCurve()Funcionalidades removidas: Point#multiplyAndAddUnsafe, Point#hasEvenY, Field.MASK
npm install && npm run build && npm test irá compilar o código e executar os testes.
Existem suites adicionais: testes lentos de escalares grandes / curvas grandes npm run test:slow,
e o harness de constância temporal npm run benchmark:ct.
Consulte paulmillr.com/noble para recursos úteis, artigos, documentação e demos relacionados com a biblioteca.
The MIT License (MIT)
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
Consulte o ficheiro LICENSE.
curve, hash-to-curve, modular, poseidon, utils, weierstrass e
módulos de nível superior _shortw_utils e secp256k1weierstrass() + ecdsa() / edwards() + eddsa().
weierstrass / edwards esperam parâmetros de curva simplificados (Fp tornou-se p);
ecdsa / eddsa esperam a classe Point e a função de hash