@noble/curves
Advanced tools
Comparing version 0.1.0 to 0.2.0
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Utilities for modular arithmetics | ||
const _0n = BigInt(0); | ||
@@ -100,3 +101,2 @@ const _1n = BigInt(1); | ||
* Calculates square root of a number in a finite field. | ||
* Used to calculate y - the square root of y². | ||
*/ | ||
@@ -150,1 +150,10 @@ export function sqrt(number, modulo) { | ||
} | ||
// Little-endian check for first LE bit (last BE bit); | ||
export const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n; | ||
// An idea on modular arithmetic for bls12-381: | ||
// const FIELD = {add, pow, sqrt, mul}; | ||
// Functions will take field elements, no need for an additional class | ||
// Could be faster. 1 bigint field will just do operations and mod later: | ||
// instead of 'r = mod(r * b, P)' we will write r = mul(r, b); | ||
// Could be insecure without shape check, so it needs to be done. | ||
// Functions could be inlined by JIT. |
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Convert between types | ||
// --------------------- | ||
export function validateOpts(curve) { | ||
for (const i of ['P', 'n', 'h', 'Gx', 'Gy']) { | ||
if (typeof curve[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
for (const i of ['nBitLength', 'nByteLength']) { | ||
if (curve[i] === undefined) | ||
continue; // Optional | ||
if (!Number.isSafeInteger(curve[i])) | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
// Set defaults | ||
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve }); | ||
} | ||
import * as mod from './modular.js'; | ||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); | ||
@@ -45,9 +58,19 @@ export function bytesToHex(uint8a) { | ||
// Big Endian | ||
export function bytesToNumber(bytes) { | ||
export function bytesToNumberBE(bytes) { | ||
return hexToNumber(bytesToHex(bytes)); | ||
} | ||
export function ensureBytes(hex) { | ||
export function bytesToNumberLE(uint8a) { | ||
if (!(uint8a instanceof Uint8Array)) | ||
throw new Error('Expected Uint8Array'); | ||
return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse())); | ||
} | ||
export const numberToBytesBE = (n, len) => hexToBytes(n.toString(16).padStart(len * 2, '0')); | ||
export const numberToBytesLE = (n, len) => numberToBytesBE(n, len).reverse(); | ||
export function ensureBytes(hex, expectedLength) { | ||
// Uint8Array.from() instead of hash.slice() because node.js Buffer | ||
// is instance of Uint8Array, and its slice() creates **mutable** copy | ||
return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex); | ||
const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex); | ||
if (typeof expectedLength === 'number' && bytes.length !== expectedLength) | ||
throw new Error(`Expected ${expectedLength} bytes`); | ||
return bytes; | ||
} | ||
@@ -69,1 +92,35 @@ // Copies several Uint8Arrays into one. | ||
} | ||
// CURVE.n lengths | ||
export function nLength(n, nBitLength) { | ||
// Bit size, byte size of CURVE.n | ||
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length; | ||
const nByteLength = Math.ceil(_nBitLength / 8); | ||
return { nBitLength: _nBitLength, nByteLength }; | ||
} | ||
/** | ||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF | ||
* and convert them into private scalar, with the modulo bias being neglible. | ||
* As per FIPS 186 B.4.1. | ||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | ||
* @param hash hash output from sha512, or a similar function | ||
* @returns valid private scalar | ||
*/ | ||
const _1n = BigInt(1); | ||
export function hashToPrivateScalar(hash, CURVE_ORDER, isLE = false) { | ||
hash = ensureBytes(hash); | ||
const orderLen = nLength(CURVE_ORDER).nByteLength; | ||
const minLen = orderLen + 8; | ||
if (orderLen < 16 || hash.length < minLen || hash.length > 1024) | ||
throw new Error('Expected valid bytes of private key as per FIPS 186'); | ||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash); | ||
return mod.mod(num, CURVE_ORDER - _1n) + _1n; | ||
} | ||
export function equalBytes(b1, b2) { | ||
// We don't care about timing attacks here | ||
if (b1.length !== b2.length) | ||
return false; | ||
for (let i = 0; i < b1.length; i++) | ||
if (b1[i] !== b2[i]) | ||
return false; | ||
return true; | ||
} |
@@ -0,1 +1,2 @@ | ||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
export declare function mod(a: bigint, b: bigint): bigint; | ||
@@ -23,4 +24,4 @@ /** | ||
* Calculates square root of a number in a finite field. | ||
* Used to calculate y - the square root of y². | ||
*/ | ||
export declare function sqrt(number: bigint, modulo: bigint): bigint; | ||
export declare const isNegativeLE: (num: bigint, modulo: bigint) => boolean; |
"use strict"; | ||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.sqrt = exports.legendre = exports.invertBatch = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0; | ||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
exports.isNegativeLE = exports.sqrt = exports.legendre = exports.invertBatch = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0; | ||
// Utilities for modular arithmetics | ||
const _0n = BigInt(0); | ||
@@ -109,3 +110,2 @@ const _1n = BigInt(1); | ||
* Calculates square root of a number in a finite field. | ||
* Used to calculate y - the square root of y². | ||
*/ | ||
@@ -160,1 +160,11 @@ function sqrt(number, modulo) { | ||
exports.sqrt = sqrt; | ||
// Little-endian check for first LE bit (last BE bit); | ||
const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n; | ||
exports.isNegativeLE = isNegativeLE; | ||
// An idea on modular arithmetic for bls12-381: | ||
// const FIELD = {add, pow, sqrt, mul}; | ||
// Functions will take field elements, no need for an additional class | ||
// Could be faster. 1 bigint field will just do operations and mod later: | ||
// instead of 'r = mod(r * b, P)' we will write r = mul(r, b); | ||
// Could be insecure without shape check, so it needs to be done. | ||
// Functions could be inlined by JIT. |
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
export declare type Hex = Uint8Array | string; | ||
export declare type PrivKey = Hex | bigint | number; | ||
export declare type BasicCurve = { | ||
P: bigint; | ||
n: bigint; | ||
nBitLength?: number; | ||
nByteLength?: number; | ||
h: bigint; | ||
Gx: bigint; | ||
Gy: bigint; | ||
}; | ||
export declare function validateOpts<T extends BasicCurve>(curve: T): Readonly<{ | ||
readonly nBitLength: number; | ||
readonly nByteLength: number; | ||
} & T>; | ||
export declare function bytesToHex(uint8a: Uint8Array): string; | ||
@@ -6,4 +21,13 @@ export declare function numberToHexUnpadded(num: number | bigint): string; | ||
export declare function hexToBytes(hex: string): Uint8Array; | ||
export declare function bytesToNumber(bytes: Uint8Array): bigint; | ||
export declare function ensureBytes(hex: string | Uint8Array): Uint8Array; | ||
export declare function bytesToNumberBE(bytes: Uint8Array): bigint; | ||
export declare function bytesToNumberLE(uint8a: Uint8Array): bigint; | ||
export declare const numberToBytesBE: (n: bigint, len: number) => Uint8Array; | ||
export declare const numberToBytesLE: (n: bigint, len: number) => Uint8Array; | ||
export declare function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array; | ||
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array; | ||
export declare function nLength(n: bigint, nBitLength?: number): { | ||
nBitLength: number; | ||
nByteLength: number; | ||
}; | ||
export declare function hashToPrivateScalar(hash: Hex, CURVE_ORDER: bigint, isLE?: boolean): bigint; | ||
export declare function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean; |
"use strict"; | ||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Convert between types | ||
// --------------------- | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.concatBytes = exports.ensureBytes = exports.bytesToNumber = exports.hexToBytes = exports.hexToNumber = exports.numberToHexUnpadded = exports.bytesToHex = void 0; | ||
exports.equalBytes = exports.hashToPrivateScalar = exports.nLength = exports.concatBytes = exports.ensureBytes = exports.numberToBytesLE = exports.numberToBytesBE = exports.bytesToNumberLE = exports.bytesToNumberBE = exports.hexToBytes = exports.hexToNumber = exports.numberToHexUnpadded = exports.bytesToHex = exports.validateOpts = void 0; | ||
function validateOpts(curve) { | ||
for (const i of ['P', 'n', 'h', 'Gx', 'Gy']) { | ||
if (typeof curve[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
for (const i of ['nBitLength', 'nByteLength']) { | ||
if (curve[i] === undefined) | ||
continue; // Optional | ||
if (!Number.isSafeInteger(curve[i])) | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
// Set defaults | ||
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve }); | ||
} | ||
exports.validateOpts = validateOpts; | ||
const mod = require("./modular.js"); | ||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); | ||
@@ -52,10 +66,23 @@ function bytesToHex(uint8a) { | ||
// Big Endian | ||
function bytesToNumber(bytes) { | ||
function bytesToNumberBE(bytes) { | ||
return hexToNumber(bytesToHex(bytes)); | ||
} | ||
exports.bytesToNumber = bytesToNumber; | ||
function ensureBytes(hex) { | ||
exports.bytesToNumberBE = bytesToNumberBE; | ||
function bytesToNumberLE(uint8a) { | ||
if (!(uint8a instanceof Uint8Array)) | ||
throw new Error('Expected Uint8Array'); | ||
return BigInt('0x' + bytesToHex(Uint8Array.from(uint8a).reverse())); | ||
} | ||
exports.bytesToNumberLE = bytesToNumberLE; | ||
const numberToBytesBE = (n, len) => hexToBytes(n.toString(16).padStart(len * 2, '0')); | ||
exports.numberToBytesBE = numberToBytesBE; | ||
const numberToBytesLE = (n, len) => (0, exports.numberToBytesBE)(n, len).reverse(); | ||
exports.numberToBytesLE = numberToBytesLE; | ||
function ensureBytes(hex, expectedLength) { | ||
// Uint8Array.from() instead of hash.slice() because node.js Buffer | ||
// is instance of Uint8Array, and its slice() creates **mutable** copy | ||
return hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex); | ||
const bytes = hex instanceof Uint8Array ? Uint8Array.from(hex) : hexToBytes(hex); | ||
if (typeof expectedLength === 'number' && bytes.length !== expectedLength) | ||
throw new Error(`Expected ${expectedLength} bytes`); | ||
return bytes; | ||
} | ||
@@ -79,1 +106,38 @@ exports.ensureBytes = ensureBytes; | ||
exports.concatBytes = concatBytes; | ||
// CURVE.n lengths | ||
function nLength(n, nBitLength) { | ||
// Bit size, byte size of CURVE.n | ||
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length; | ||
const nByteLength = Math.ceil(_nBitLength / 8); | ||
return { nBitLength: _nBitLength, nByteLength }; | ||
} | ||
exports.nLength = nLength; | ||
/** | ||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF | ||
* and convert them into private scalar, with the modulo bias being neglible. | ||
* As per FIPS 186 B.4.1. | ||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | ||
* @param hash hash output from sha512, or a similar function | ||
* @returns valid private scalar | ||
*/ | ||
const _1n = BigInt(1); | ||
function hashToPrivateScalar(hash, CURVE_ORDER, isLE = false) { | ||
hash = ensureBytes(hash); | ||
const orderLen = nLength(CURVE_ORDER).nByteLength; | ||
const minLen = orderLen + 8; | ||
if (orderLen < 16 || hash.length < minLen || hash.length > 1024) | ||
throw new Error('Expected valid bytes of private key as per FIPS 186'); | ||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash); | ||
return mod.mod(num, CURVE_ORDER - _1n) + _1n; | ||
} | ||
exports.hashToPrivateScalar = hashToPrivateScalar; | ||
function equalBytes(b1, b2) { | ||
// We don't care about timing attacks here | ||
if (b1.length !== b2.length) | ||
return false; | ||
for (let i = 0; i < b1.length; i++) | ||
if (b1[i] !== b2[i]) | ||
return false; | ||
return true; | ||
} | ||
exports.equalBytes = equalBytes; |
{ | ||
"name": "@noble/curves", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Minimal, zero-dependency JS implementation of elliptic curve cryptography", | ||
@@ -11,3 +11,3 @@ "files": [ | ||
"scripts": { | ||
"bench": "node test/benchmark/index.js", | ||
"bench": "node curve-definitions/benchmark/index.js", | ||
"build": "tsc && tsc -p tsconfig.esm.json", | ||
@@ -36,2 +36,7 @@ "build:release": "rollup -c rollup.config.js", | ||
"exports": { | ||
"./edwards": { | ||
"types": "./lib/edwards.d.ts", | ||
"import": "./lib/esm/edwards.js", | ||
"default": "./lib/edwards.js" | ||
}, | ||
"./modular": { | ||
@@ -42,7 +47,12 @@ "types": "./lib/modular.d.ts", | ||
}, | ||
"./shortw": { | ||
"types": "./lib/shortw.d.ts", | ||
"import": "./lib/esm/shortw.js", | ||
"default": "./lib/shortw.js" | ||
"./montgomery": { | ||
"types": "./lib/montgomery.d.ts", | ||
"import": "./lib/esm/montgomery.js", | ||
"default": "./lib/montgomery.js" | ||
}, | ||
"./weierstrass": { | ||
"types": "./lib/weierstrass.d.ts", | ||
"import": "./lib/esm/weierstrass.js", | ||
"default": "./lib/weierstrass.js" | ||
}, | ||
"./utils": { | ||
@@ -59,2 +69,8 @@ "types": "./lib/utils.d.ts", | ||
"hyperelliptic", | ||
"weierstrass", | ||
"edwards", | ||
"montgomery", | ||
"secp256k1", | ||
"ed25519", | ||
"ed448", | ||
"p256", | ||
@@ -64,6 +80,2 @@ "p384", | ||
"nist", | ||
"weierstrass", | ||
"edwards", | ||
"montgomery", | ||
"hashes", | ||
"ecc", | ||
@@ -80,2 +92,2 @@ "ecdsa", | ||
] | ||
} | ||
} |
114
README.md
@@ -5,11 +5,17 @@ # noble-curves | ||
Implements Short Weierstrass curves with ECDSA signature scheme. | ||
- Short Weierstrass curve with ECDSA signatures | ||
- Twisted Edwards curve with EdDSA signatures | ||
- Montgomery curve for ECDH key agreement | ||
To keep the package minimal, no curve definitions are provided out-of-box. | ||
Main reason for that is the fact hashing library is usually required for full functionality. Use separate package that defines popular curves: `micro-curve-definitions` for P192, P224, P256, P384, P521, secp256k1, stark curve, bn254, pasta (pallas/vesta) - it depends on `@noble/hashes`. | ||
To keep the package minimal, no curve definitions are provided out-of-box. Use `micro-curve-definitions` module: | ||
- It provides P192, P224, P256, P384, P521, secp256k1, stark curve, bn254, pasta (pallas/vesta) short weierstrass curves | ||
- It also provides ed25519 and ed448 twisted edwards curves | ||
- Main reason for separate package is the fact hashing library (like `@noble/hashes`) is required for full functionality | ||
- We may reconsider merging packages in future, when a stable version would be ready | ||
Future plans: | ||
- Edwards, Twisted Edwards & Montgomery curves | ||
- hash-to-curve standard | ||
- hash to curve standard | ||
- point indistinguishability | ||
- pairings | ||
@@ -44,4 +50,3 @@ | ||
```ts | ||
// Short Weierstrass curve | ||
import shortw from '@noble/curves/shortw'; | ||
import { weierstrass } from '@noble/curves/weierstrass'; // Short Weierstrass curve | ||
import { sha256 } from '@noble/hashes/sha256'; | ||
@@ -51,10 +56,7 @@ import { hmac } from '@noble/hashes/hmac'; | ||
export const secp256k1 = shortw({ | ||
const secp256k1 = weierstrass({ | ||
a: 0n, | ||
b: 7n, | ||
// Field over which we'll do calculations | ||
P: 2n ** 256n - 2n ** 32n - 2n ** 9n - 2n ** 8n - 2n ** 7n - 2n ** 6n - 2n ** 4n - 1n, | ||
// Curve order, total count of valid points in the field | ||
P: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn, | ||
n: 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n, | ||
// Base point (x, y) aka generator point | ||
Gx: 55066263022277343669578718895168534326250603453777594175500187360389116729240n, | ||
@@ -64,12 +66,94 @@ Gy: 32670510020758816978083085130507043184471273380659243275938904335757337482424n, | ||
hmac: (k: Uint8Array, ...msgs: Uint8Array[]) => hmac(sha256, key, concatBytes(...msgs)), | ||
randomBytes: randomBytes | ||
randomBytes | ||
}); | ||
// secp256k1.getPublicKey(priv) | ||
// secp256k1.sign(msg, priv) | ||
secp256k1.getPublicKey(secp256k1.utils.randomPrivateKey()); | ||
secp256k1.sign(randomBytes(32), secp256k1.utils.randomPrivateKey()); | ||
// secp256k1.verify(sig, msg, pub) | ||
import { twistedEdwards } from '@noble/curves/edwards'; // Twisted Edwards curve | ||
import { sha512 } from '@noble/hashes/sha512'; | ||
const ed25519 = twistedEdwards({ | ||
a: -1n, | ||
d: 37095705934669439343138083508754565189542113879843219016388785533085940283555n, | ||
P: 57896044618658097711785492504343953926634992332820282019728792003956564819949n, | ||
n: 7237005577332262213973186563042994240857116359379907606001950938285454250989n, | ||
h: 8n, | ||
Gx: 15112221349535400772501151409588531511454012693041857206046113283949847762202n, | ||
Gy: 46316835694926478169428394003475163141307993866256225615783033603165251855960n, | ||
hash: sha512, | ||
randomBytes, | ||
adjustScalarBytes(bytes) { // could be no-op | ||
bytes[0] &= 248; | ||
bytes[31] &= 127; | ||
bytes[31] |= 64; | ||
return bytes; | ||
}, | ||
} as const); | ||
ed25519.getPublicKey(ed25519.utils.randomPrivateKey()); | ||
``` | ||
## Performance | ||
Benchmark results on Apple M2 with node v18.10: | ||
``` | ||
==== secp256k1 ==== | ||
- getPublicKey1 (samples: 10000) | ||
noble_old x 8,131 ops/sec @ 122μs/op | ||
secp256k1 x 7,374 ops/sec @ 135μs/op | ||
- getPublicKey255 (samples: 10000) | ||
noble_old x 7,894 ops/sec @ 126μs/op | ||
secp256k1 x 7,327 ops/sec @ 136μs/op | ||
- sign (samples: 5000) | ||
noble_old x 5,243 ops/sec @ 190μs/op | ||
secp256k1 x 4,834 ops/sec @ 206μs/op | ||
- getSharedSecret (samples: 1000) | ||
noble_old x 653 ops/sec @ 1ms/op | ||
secp256k1 x 634 ops/sec @ 1ms/op | ||
- verify (samples: 1000) | ||
secp256k1_old x 1,038 ops/sec @ 962μs/op | ||
secp256k1 x 1,009 ops/sec @ 990μs/op | ||
==== ed25519 ==== | ||
- getPublicKey (samples: 10000) | ||
old x 8,632 ops/sec @ 115μs/op | ||
noble x 8,390 ops/sec @ 119μs/op | ||
- sign (samples: 5000) | ||
old x 4,376 ops/sec @ 228μs/op | ||
noble x 4,233 ops/sec @ 236μs/op | ||
- verify (samples: 1000) | ||
old x 865 ops/sec @ 1ms/op | ||
noble x 860 ops/sec @ 1ms/op | ||
==== ed448 ==== | ||
- getPublicKey (samples: 5000) | ||
noble x 3,224 ops/sec @ 310μs/op | ||
- sign (samples: 2500) | ||
noble x 1,561 ops/sec @ 640μs/op | ||
- verify (samples: 500) | ||
noble x 313 ops/sec @ 3ms/op | ||
==== nist ==== | ||
- getPublicKey (samples: 2500) | ||
P256 x 7,993 ops/sec @ 125μs/op | ||
P384 x 3,819 ops/sec @ 261μs/op | ||
P521 x 2,074 ops/sec @ 481μs/op | ||
- sign (samples: 1000) | ||
P256 x 5,327 ops/sec @ 187μs/op | ||
P384 x 2,728 ops/sec @ 366μs/op | ||
P521 x 1,594 ops/sec @ 626μs/op | ||
- verify (samples: 250) | ||
P256 x 806 ops/sec @ 1ms/op | ||
P384 x 353 ops/sec @ 2ms/op | ||
P521 x 171 ops/sec @ 5ms/op | ||
==== stark ==== | ||
- pedersen (samples: 500) | ||
old x 85 ops/sec @ 11ms/op | ||
noble x 1,216 ops/sec @ 822μs/op | ||
- verify (samples: 500) | ||
old x 302 ops/sec @ 3ms/op | ||
noble x 698 ops/sec @ 1ms/op | ||
``` | ||
## License | ||
MIT (c) Paul Miller [(https://paulmillr.com)](https://paulmillr.com), see LICENSE file. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
196744
23
4625
156
1