
감사된 & 최소한의 타원 곡선 암호화 JS 구현.
감사된 & 최소한의 타원 곡선 암호 구현 JS.
곡선에는 5kb 자매 프로젝트가 있습니다 secp256k1 & ed25519. 이들은 공격 표면이 더 작지만 기능이 적습니다.
noble 암호학 — 높은 보안성, 쉽게 감사할 수 있는 포함된 암호 라이브러리 및 도구 세트.
npm install @noble/curves
deno add jsr:@noble/curves
우리는 모든 주요 플랫폼과 런타임을 지원합니다. React Native의 경우 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 aka 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-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);
Messages는 항상 먼저 해시됩니다: prehashed signing을 참조하세요. ECDSA는 결정적 k를 사용하고, EdDSA는 RFC 8032를 따르며, Schnorr(secp256k1 전용)는 BIP 340을 따릅니다: Specs를 참조하세요.
MuSig2 서명 방식과 secp256k1용 BIP324 ElligatorSwift 매핑은 별도 패키지에서 사용할 수 있습니다.
import { ristretto255, ristretto255_hasher, ristretto255_oprf } from '@noble/curves/ed25519.js'; import { decaf448, decaf448_hasher, decaf448_oprf } from '@noble/curves/ed448.js';
console.log(ristretto255.Point, decaf448.Point);
[RFC 9496](https://www.rfc-editor.org/rfc/rfc9496)에서 ristretto255 및 decaf448에 대한 더 많은 정보를 확인하세요.
[Point](#elliptic-curve-point-math), [hasher](#hash-to-curve-hashing-to-curve-points) 및 [oprf](#oprfs)에 대한 별도 문서를 확인하세요.
#### 사전 해시 서명```js
import { secp256k1 } from '@noble/curves/secp256k1.js';
import { keccak_256 } from '@noble/hashes/sha3.js';
const { secretKey } = secp256k1.keygen();
const msg = new TextEncoder().encode('hello noble');
// prehash: true (default) - hash using secp256k1.hash (sha256)
const sig = secp256k1.sign(msg, secretKey);
// prehash: false - hash using custom hash
const sigKeccak = secp256k1.sign(keccak_256(msg), secretKey, { prehash: false });
기본적으로(prehash: true), sign()과 verify()는 먼저 메시지에 곡선의 내장 해시를 적용합니다:
secp256k1에는 sha256, p521에는 sha512. prehash: false를 사용하면 사용자 정의 해시를 사용할 수 있습니다
(예: secp256k1 + keccak_256). noble-curves v1에서는 prehash: false가 기본값이었습니다.
import { secp256k1 } from '@noble/curves/secp256k1.js'; const { secretKey, publicKey } = secp256k1.keygen(); const msg = new TextEncoder().encode('hello noble'); const sigRec = secp256k1.sign(msg, secretKey, { format: 'recovered' }); const publicKey_ = secp256k1.recoverPublicKey(sigRec, msg); // == publicKey
// recovered sig is compact sig with an extra byte const sigNoRec = secp256k1.sign(msg, secretKey, { format: 'compact' }); // sigNoRec == sigRec.slice(1)
// Signature instance const sigInstance = secp256k1.Signature.fromBytes(sigRec, 'recovered');
Public key recovery는 ECDSA에서만 지원됩니다. 이는 단순한 수학 연산입니다:
서명이 실제로 수행되었다는 보장은 없습니다. 위조된 (r, s, h)는
임의의 공개 키로 복구되지만, 이 특정 위조된 h로 이어질 m을 찾는 것은 실행 가능하지 않습니다.
#### 노이즈를 사용한 Hedged 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과 같은 최신 방식은 서명 생성에
무작위성을 도입합니다 - 일명 헤징(hedging)이라고 합니다. extraEntropy는 헤지드 모드를 활성화합니다. 더 많은 정보는
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` (기본값)는 [ZIP215](https://zips.z.cash/zip-0215)에 정의된 더 관대하고 [합의 친화적인](https://hdevalence.ca/blog/2020-10-04-its-25519am) 검증 규칙을 사용합니다.
* `zip215: false`는 엄격한 RFC 8032 / FIPS 186-5 검증을 적용하고 SBS 기반
부인 방지를 추가하는데, 이는 계약 서명, 전자 투표 및 블록체인에 유용합니다.
두 모드 모두 SUF-CMA(선택 메시지 공격에 대한 강한 위조 불가능성)를 갖추고 있습니다;
대부분의 다른 라이브러리는 SUF-CMA도 SBS도 갖추고 있지 않습니다.
자세한 내용은 [Taming the many EdDSAs](https://eprint.iacr.org/2020/1244)를 참조하세요.
### ECDH: Diffie-Hellman 공유 비밀```js
import { x25519 } from '@noble/curves/ed25519.js';
const alice = x25519.keygen();
const bob = x25519.keygen();
const sharedKey = x25519.getSharedSecret(alice.secretKey, bob.publicKey);
// Same API: secp256k1, p256, p384, p521, x448
// converting ed25519 keys to x25519
import { ed25519 } from '@noble/curves/ed25519.js';
const alice2 = ed25519.keygen();
const bob2 = ed25519.keygen();
const aliceSecX = ed25519.utils.toMontgomerySecret(alice2.secretKey);
const bobPubX = ed25519.utils.toMontgomery(bob2.publicKey);
const sharedKey2 = x25519.getSharedSecret(aliceSecX, bobPubX);
우리는 모든 Weierstrass 곡선과 2개의 Montgomery 곡선 X25519 (Curve25519) & X448 (Curve448)에 대한 ECDH를 제공하며, RFC 7748을 준수합니다.
Weierstrass 곡선에서 공유 비밀은:
key.slice(1)을 사용하여 제거하세요sha256(shared) 또는 hkdf(shared)와 같이 해싱이나 KDF를 위에 적용하세요import { ed25519, x25519 } from '@noble/curves/webcrypto.js';
// signatures: p256, p384, p521, ed25519, ed448 const keys = await ed25519.keygen(); const msg = new TextEncoder().encode('hello noble'); const sig = await ed25519.sign(msg, keys.secretKey); const isValid = await ed25519.verify(sig, msg, keys.publicKey);
// ECDH: p256, p384, p521, x25519, x448 const alice = await x25519.keygen(); const bob = await x25519.keygen(); const shared = await x25519.getSharedSecret(alice.secretKey, bob.publicKey);
// key conversion between noble (raw) and webcrypto (pkcs8 / spki) formats import { p256 as p256n } from '@noble/curves/nist.js'; import { p256 } from '@noble/curves/webcrypto.js'; const nobleKeys = p256n.keygen(); const secretKeyPkcs8 = await p256.utils.convertSecretKey(nobleKeys.secretKey, 'raw', 'pkcs8'); const publicKeySpki = await p256.utils.convertPublicKey(nobleKeys.publicKey, 'raw', 'spki');
WebCrypto 내장 기능을 감싼 얇은 래퍼로, noble API를 그대로 따릅니다. 메서드는 항상 비동기이며,
런타임 지원은 다양하므로 `await curve.isSupported()`로 확인하세요.
순수 JS 키 변환 유틸리티는 [micro-key-producer](https://github.com/paulmillr/micro-key-producer)를
확인하세요.
### 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
예를 들어 사용법은 BLS EVM 프리컴파일 구현을 확인하세요.
BN254 API는 bls12-381을 미러링합니다. 이 곡선은 이전에 alt_bn128이라고 불렸습니다. 구현은 EIP-196 및 EIP-197과 호환됩니다: bn254 EVM 프리컴파일 구현을 확인하세요. bn254 포인트는 toBytes를 구현하지 않습니다, 직렬화 표준이 없기 때문입니다: 구현마다 엔디안, 플래그, G2 허수부 순서가 다릅니다. 대신 bigint에서 포인트를 초기화하세요.
import { secp256k1_hasher } from '@noble/curves/secp256k1.js';
const msg = Uint8Array.from([0xca, 0xfe, 0x01, 0x23]); const point = secp256k1_hasher.hashToCurve(msg); const pointDst = secp256k1_hasher.hashToCurve(msg, { DST: 'hello noble' }); const pointNu = secp256k1_hasher.encodeToCurve(msg); const scalar = secp256k1_hasher.hashToScalar(msg);
// Same API: p256_hasher, p384_hasher, p521_hasher (nist.js), // ed25519_hasher, ristretto255_hasher (ed25519.js), ed448_hasher, decaf448_hasher (ed448.js), // bls12_381.G1, bls12_381.G2. // ristretto255 & decaf448 also provide deriveToCurve.
// abstract methods import { expand_message_xmd, expand_message_xof, hash_to_field } from '@noble/curves/abstract/hash-to-curve.js';
모듈은 임의의 문자열을 타원 곡선 점으로 해시할 수 있게 해줍니다. [RFC 9380](https://www.rfc-editor.org/rfc/rfc9380)을 구현합니다.
`_hasher` 네임스페이스는 트리 셰이킹을 위해 곡선과 분리되어 있습니다:
hash-to-curve가 필요 없는 사용자는 빌드에 포함되지 않습니다.
### OPRFs```js
import { p256_oprf, p384_oprf, p521_oprf } from '@noble/curves/nist.js';
import { ristretto255_oprf } from '@noble/curves/ed25519.js';
import { decaf448_oprf } from '@noble/curves/ed448.js';
우리는 RFC 9497을 준수하는 OPRF(oblivious pseudorandom function)를 제공합니다.
OPRF를 사용하면 Output = PRF(Input, serverSecretKey)를 대화식으로 생성할 수 있습니다:
FROST는 RFC 9591 임계 Schnorr 서명을 구현합니다.
애플리케이션 관점에서 이는 멀티시그와 유사합니다: max 참여자 중 임의의 min 명이 공유 공개 키 아래에서 하나의 Schnorr 서명을 공동으로 생성할 수 있습니다.
지원되는 암호 스위트는 p256_FROST, ed25519_FROST, ed448_FROST, ristretto255_FROST, secp256k1_FROST, 그리고 schnorr_FROST(Taproot 호환 secp256k1)입니다.
서명은 두 라운드로 진행됩니다: 선택된 서명자들이 먼저 커밋한 다음 서명 공유를 생성합니다.```js
import { p256_FROST } from '@noble/curves/nist.js';
const signers = { min: 2, max: 3 }; const alice = p256_FROST.Identifier.derive('[email protected]'); const bob = p256_FROST.Identifier.derive('[email protected]'); const carol = p256_FROST.Identifier.derive('[email protected]'); // trusted dealer const deal = p256_FROST.trustedDealer(signers, [alice, bob, carol]); for (const id of [alice, bob, carol]) p256_FROST.validateSecret(deal.secretShares[id], deal.public);
const msg = new TextEncoder().encode('hello threshold'); // round 1: selected signers commit const aliceRound1 = p256_FROST.commit(deal.secretShares[alice]); const bobRound1 = p256_FROST.commit(deal.secretShares[bob]); const commitmentList = [aliceRound1.commitments, bobRound1.commitments]; // round 2: signers produce signature shares const sigShares = { [alice]: https://raw.githubusercontent.com/paulmillr/noble-curves/main/p256_FROST.signShare( deal.secretShares[alice], deal.public, aliceRound1.nonces, commitmentList, msg ), [bob]: https://raw.githubusercontent.com/paulmillr/noble-curves/main/p256_FROST.signShare( deal.secretShares[bob], deal.public, bobRound1.nonces, commitmentList, msg ), }; const sig = p256_FROST.aggregate(deal.public, commitmentList, msg, sigShares); const isValid = p256_FROST.verify(sig, msg, deal.public.commitments[0]);
키 생성은 신뢰할 수 있는 딜러(위 참조) 또는 DKG(분산 키 생성)를 통해 수행할 수 있습니다.
DKG는 세 라운드로 구성됩니다: 참여자들이 키 생성에 커밋하고, 비공개 공유를 교환한 뒤,
최종 참여자 키를 도출합니다 - [테스트](https://github.com/paulmillr/noble-curves/blob/main/test/rfc9591-frost.test.ts)에서 `DKG.round1` / `round2` / `round3` 사용법을 참조하세요.
이 라이브러리는 암호화 단계를 구현하며, 주변 애플리케이션 프로토콜은 구현하지 않습니다:
호출자는 여전히 인증된 통신, 조정, 재시도, 세션 처리, 정책을 직접 처리해야 합니다.
### poseidon: Poseidon 해시
ZK 친화적 해시인 [Poseidon](https://www.poseidon-hash.info)을 구현합니다:
순열과 스폰지.
서로 다른 상수를 가진 많은 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
모든 산술 연산은 modular 서브모듈에서 정의된 유한체 상에서 JS bigint로 수행됩니다.
체 연산은 상수 시간이 아닙니다: security를 참조하세요.
이 사실은 대부분 무관하지만, 염두에 두어야 할 중요한 메서드는 pow이며,
이는 순진하게 사용될 경우 지수 비트를 누출할 수 있습니다.
import { weierstrass, ecdsa } from '@noble/curves/abstract/weierstrass.js'; import { sha256 } from '@noble/hashes/sha2.js'; // NIST secp192r1 aka p192. https://www.secg.org/sec2-v2.pdf const p192_CURVE = { p: 0xfffffffffffffffffffffffffffffffeffffffffffffffffn, n: 0xffffffffffffffffffffffff99def836146bc9b1b4d22831n, h: 1n, a: 0xfffffffffffffffffffffffffffffffefffffffffffffffcn, b: 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1n, Gx: 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012n, Gy: 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811n, }; const p192_Point = weierstrass(p192_CURVE); const p192 = ecdsa(p192_Point, sha256);
const keys = p192.keygen(); const msg = new TextEncoder().encode('custom curve'); const sig = p192.sign(msg, keys.secretKey); const isValid = p192.verify(sig, msg, keys.publicKey);
Short Weierstrass 곡선의 공식은 `y² = x³ + ax + b`입니다. `weierstrass`는
인수 `a`, `b`, 체 특성 `p`, 곡선 위수 `n`,
공동인자 `h` 그리고 생성점의 좌표 `Gx`, `Gy`를 받아서 Point 클래스를 반환합니다.
`ecdsa`는 Point 클래스와 해시 함수를 결합하여 서명 스킴으로 만듭니다.
#### edwards: 사용자 정의 Edwards 곡선```js
import { edwards } from '@noble/curves/abstract/edwards.js';
const ed25519_CURVE = {
p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
h: 8n,
a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,
};
const ed25519_Point = edwards(ed25519_CURVE);
Twisted Edwards 곡선의 공식은 ax² + y² = 1 + dx²y²입니다.
a, d, 필드 특성 p, 곡선 위수 n(때때로 L로 명명됨),
공동인자 h, 그리고 생성점의 좌표 Gx, Gy를 지정해야 합니다.
이 라이브러리는 감사를 받았습니다:
curve, modular, poseidon, weierstrass2026년 4월부터 정기적인 AI 지원 자체 감사를 시작했습니다.
속성 기반, 교차 라이브러리 및 Wycheproof 벡터에 대해 테스트되며, github ci에서 퍼징됩니다.
이상한 점이 보이면: 조사하고 보고하세요.
우리는 알고리즘적 상수 시간을 목표로 합니다. _JIT 컴파일러_와 _가비지 컬렉터_는 스크립팅 언어에서 "상수 시간" 타이밍 공격 저항을 달성하기 극히 어렵게 만듭니다. 이는 _다른 어떤 JS 라이브러리도 상수 시간성을 가질 수 없다_는 것을 의미합니다. GC가 없는 정적 타입 언어인 Rust조차도 일부 경우에 상수 시간 달성을 더 어렵게 만듭니다. 절대적인 보안이 목표라면, 네이티브 바인딩을 포함한 어떤 JS 라이브러리도 사용하지 마세요. 저수준 라이브러리와 언어를 사용하세요.
이러한 한계 내에서, 비밀 스칼라 곱셈은 구체적이고 측정 가능한 속성을 제공합니다:
multiply()는 데이터 무관 테이블 스캔을 사용하는
부호 있는 고정 윈도우 테이블을 사용합니다 — 점 연산의 수와 순서가
스칼라 값과 무관합니다.r로 s + r·n으로 추가 마스킹됩니다. 이는 공동인자-1
곡선(p256, p384, p521, secp256k1)의 모든 곱셈과 모든 곳의 기저점 곱셈에 적용됩니다.benchmark/ct.ts)가
적대적 스칼라 클래스(희소 vs 밀집, 낮은 비트 vs 높은 비트,
위수 근처, 비트 패턴) 간의 타이밍을 비교합니다. 기저점 곱셈은 어떤
곡선에서도 구별 가능한 타이밍을 보이지 않으며, 임의점 곱셈은 Weierstrass 곡선에서
아무것도 보이지 않습니다(1000 샘플에서 최대 |t| ≤ 2.8; 임계값 4.5).알려진 한계: 공동인자가 있는 Edwards 곡선(ed25519, ed448)에서 비기저 점을 비밀 스칼라로 곱하는 것은 블라인딩되지 않습니다. 동일한 하네스가 이를 안정적으로 감지합니다. EdDSA 서명은 영향을 받지 않으며(블라인딩된 기저점만 곱함), X25519/X448은 별도의 Montgomery-래더 구현을 사용합니다(역시 영향 없음). 이는 임의의 Edwards/Ristretto 점을 장기 비밀 스칼라로 곱하는 프로토콜에 중요합니다. 그런 곳에서는 구성상 전체 폭인 스칼라를 선호하세요. 격리된 하네스에서의 감지 가능성이 실제 악용 가능성을 의미하지는 않는다는 점에 유의하세요: 우리는 현실적인 교차 테넌트 / 브라우저 내 설정에서 스칼라 추출을 시도했고 100,000개의 타이밍 샘플로도 Edwards 스칼라를 복구할 수 없었습니다.
절대적인 보안이 목표라면 JS / WASM 대신 저수준 언어를 사용하세요.
이 라이브러리는 주로 Uint8Array와 bigint를 사용합니다.
.fill(0)이 있지만
JS에서는 보장이 없습니다await fn()은 모든 내부 변수를 메모리에 기록합니다. 비동기
함수에서는 코드 청크가 언제 실행될지 보장이 없습니다. 이는 공격자가
메모리에서 데이터를 읽을 충분한 시간을 가질 수 있음을 의미합니다.이는 일부 비밀이 예상보다 오래 메모리에 남을 수 있음을 의미합니다. 그러나 공격자가 애플리케이션 메모리를 읽을 수 있다면 어차피 끝장입니다: 프로세스 메모리를 덤프하고 민감한 데이터가 남아 있지 않은지 검증하는 복잡한 테스트 스위트 없이는 민감한 데이터 제로화에 대해 아무것도 보장할 방법이 없습니다. JS의 경우 이는 모든 브라우저(모바일 포함)를 테스트하는 것을 의미합니다. 그리고 물론, 라이브러리를 소비하는 실제 애플리케이션에서 동일한 테스트 스위트를 사용하지 않으면 무용지물입니다.
이 패키지에는 1개의 의존성과 몇 개의 개발 의존성이 있습니다:
우리는 암호학적으로 안전한 PRNG로 간주되는 내장
crypto.getRandomValues에
의존합니다.
브라우저는 과거에 약점을 가졌었고 - 다시 가질 수도 있습니다 - 하지만 사용자 공간 CSPRNG를 구현하는 것은 고품질 엔트로피의 신뢰할 수 있는 사용자 공간 소스가 없기 때문에 훨씬 더 나쁩니다.
암호학적으로 관련된 양자 컴퓨터가 구축된다면, Shor 알고리즘을 사용하여 타원 곡선 암호(ECDSA / EdDSA 및 ECDH 모두)를 깰 수 있게 됩니다.
SPHINCS+와 같은 더 새롭거나 하이브리드 알고리즘으로 전환하는 것을 고려하세요. 이들은 noble-post-quantum에서 사용할 수 있습니다.
NIST는 2035년 이후 고전 암호(RSA, DSA, ECDSA, ECDH)를 금지합니다. 호주 ASD는 2030년 이후 금지합니다.
npm run benchmark
noble-curves는 20MB 이상의 베이스 포인트 사전 계산을 생성하는 데 10ms 이상을 소비합니다.
이는 곡선당 **한 번만** 수행됩니다.
생성은 어떤 메서드(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로 업그레이드하세요. 사용 중단(deprecation) 경고를 수정한 다음 v2로 전환하세요.
모듈:
.js 확장자가 필요합니다: @noble/curves/ed25519 => @noble/curves/ed25519.js.
이를 통해 트랜스파일러 없이 네이티브 브라우저 사용이 가능합니다.p256, p384, p521은 nist로 이동되었고, jubjub은 misc로 이동되었습니다.pasta와 bn254_weierstrass (페어링 기반이 아닌 bn254) 곡선은 제거되었습니다.새로운 기능:
isValidSecretKey, isValidPublicKey 메서드호환성을 깨는 변경 사항:
Point.fromHex는 이제 문자열 전용입니다: Uint8Array에는 Point.fromBytes를 사용하세요.{prehash: false}{lowS: false}{format: 'der'}에 명시적으로 지정해야 합니다.
이는 가변성을 줄입니다.signature.toBytes()를 호출하세요.longSignatures (G1 공개키, G2 서명) 및 shortSignatures (G1 서명, G2 공개키){message: ..., publicKey: ...}[]을 기대합니다.weierstrass() + ecdsa() / .
weierstrass / edwards는 단순화된 곡선 매개변수를 기대합니다 (Fp가 p가 됨);
ecdsa / eddsa는 Point 클래스와 해시를 기대합니다.이름 변경 (curves v1.9는 이전 이름을 사용 중단으로 표시):
abstract/curve.js 서브모듈의 별도 메서드Point.BASE.multiply(Point.Fn.fromBytes(key))CURVE 속성 => 곡선 매개변수만 제공하는
Point.CURVE()*curve*_hasher.
예: secp256k1.hashToCurve => secp256k1_hasher.hashToCurve()제거된 기능: Point#multiplyAndAddUnsafe, Point#hasEvenY, Field.MASK
npm install && npm run build && npm test는 코드를 빌드하고 테스트를 실행합니다.
추가 스위트가 있습니다: 느린 대형 스칼라 / 대형 곡선 테스트 npm run test:slow,
그리고 상수 시간(constant-timeness) 하네스 npm run benchmark:ct.
라이브러리와 관련된 유용한 리소스, 문서, 기사 및 데모는 paulmillr.com/noble을 참조하세요.
The MIT License (MIT)
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
LICENSE 파일을 참조하세요.
modularposeidonutilsweierstrass_shortw_utilssecp256k1edwards() + eddsa()