@noble/curves
Advanced tools
Comparing version 0.3.2 to 0.4.0
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import * as mod from './modular.js'; | ||
import { BasicCurve, Hex, PrivKey } from './utils.js'; | ||
@@ -10,7 +11,7 @@ import { Group, GroupConstructor } from './group.js'; | ||
}; | ||
export declare type CurveType = BasicCurve & { | ||
export declare type CurveType = BasicCurve<bigint> & { | ||
a: bigint; | ||
d: bigint; | ||
hash: CHash; | ||
randomBytes?: (bytesLength?: number) => Uint8Array; | ||
randomBytes: (bytesLength?: number) => Uint8Array; | ||
adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; | ||
@@ -27,11 +28,14 @@ domain?: (data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array; | ||
readonly nByteLength: number; | ||
readonly P: bigint; | ||
readonly Fp: mod.Field<bigint>; | ||
readonly n: bigint; | ||
readonly h: bigint; | ||
readonly hEff?: bigint | undefined; | ||
readonly Gx: bigint; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly d: bigint; | ||
readonly hash: CHash; | ||
randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
readonly adjustScalarBytes?: ((bytes: Uint8Array) => Uint8Array) | undefined; | ||
@@ -97,4 +101,4 @@ readonly domain?: ((data: Uint8Array, ctx: Uint8Array, phflag: boolean) => Uint8Array) | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint) => bigint; | ||
invert: (number: bigint, modulo?: bigint) => bigint; | ||
mod: (a: bigint) => bigint; | ||
invert: (number: bigint) => bigint; | ||
randomPrivateKey: () => Uint8Array; | ||
@@ -101,0 +105,0 @@ getExtendedPublicKey: (key: PrivKey) => { |
@@ -0,3 +1,6 @@ | ||
"use strict"; | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Implementation of Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y² | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.twistedEdwards = void 0; | ||
// Differences from @noble/ed25519 1.7: | ||
@@ -10,5 +13,5 @@ // 1. Different field element lengths in ed448: | ||
// 5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448 | ||
import * as mod from './modular.js'; | ||
import { bytesToHex, concatBytes, ensureBytes, numberToBytesLE, bytesToNumberLE, hashToPrivateScalar, validateOpts as utilOpts, randomBytes as utilRandomBytes, } from './utils.js'; // TODO: import * as u from './utils.js'? | ||
import { wNAF } from './group.js'; | ||
const mod = require("./modular.js"); | ||
const utils_js_1 = require("./utils.js"); // TODO: import * as u from './utils.js'? | ||
const group_js_1 = require("./group.js"); | ||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n | ||
@@ -21,3 +24,3 @@ const _0n = BigInt(0); | ||
function validateOpts(curve) { | ||
const opts = utilOpts(curve); | ||
const opts = (0, utils_js_1.validateOpts)(curve); | ||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen)) | ||
@@ -29,3 +32,7 @@ throw new Error('Invalid hash function'); | ||
} | ||
for (const fn of ['adjustScalarBytes', 'domain', 'randomBytes', 'uvRatio']) { | ||
for (const fn of ['randomBytes']) { | ||
if (typeof opts[fn] !== 'function') | ||
throw new Error(`Invalid ${fn} function`); | ||
} | ||
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio']) { | ||
if (opts[fn] === undefined) | ||
@@ -37,9 +44,10 @@ continue; // Optional | ||
// Set defaults | ||
return Object.freeze({ randomBytes: utilRandomBytes, ...opts }); | ||
return Object.freeze({ ...opts }); | ||
} | ||
// NOTE: it is not generic twisted curve for now, but ed25519/ed448 generic implementation | ||
export function twistedEdwards(curveDef) { | ||
function twistedEdwards(curveDef) { | ||
const CURVE = validateOpts(curveDef); | ||
const Fp = CURVE.Fp; | ||
const CURVE_ORDER = CURVE.n; | ||
const fieldLen = CURVE.nByteLength; // 32 (length of one field element) | ||
const fieldLen = Fp.BYTES; // 32 (length of one field element) | ||
if (fieldLen > 2048) | ||
@@ -51,8 +59,8 @@ throw new Error('Field lengths over 2048 are not supported'); | ||
// Function overrides | ||
const { P, randomBytes } = CURVE; | ||
const modP = (a) => mod.mod(a, P); | ||
const { randomBytes } = CURVE; | ||
const modP = Fp.create; | ||
// sqrt(u/v) | ||
function _uvRatio(u, v) { | ||
try { | ||
const value = mod.sqrt(u * mod.invert(v, P), P); | ||
const value = Fp.sqrt(u * Fp.invert(v)); | ||
return { isValid: true, value }; | ||
@@ -97,3 +105,3 @@ } | ||
static toAffineBatch(points) { | ||
const toInv = mod.invertBatch(points.map((p) => p.z), P); | ||
const toInv = Fp.invertBatch(points.map((p) => p.z)); | ||
return points.map((p, i) => p.toAffine(toInv[i])); | ||
@@ -238,3 +246,3 @@ } | ||
if (invZ == null) | ||
invZ = is0 ? _8n : mod.invert(z, P); // 8 was chosen arbitrarily | ||
invZ = is0 ? _8n : Fp.invert(z); // 8 was chosen arbitrarily | ||
const ax = modP(x * invZ); | ||
@@ -252,3 +260,3 @@ const ay = modP(y * invZ); | ||
ExtendedPoint.ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n); | ||
const wnaf = wNAF(ExtendedPoint, groupLen * 8); | ||
const wnaf = (0, group_js_1.wNAF)(ExtendedPoint, groupLen * 8); | ||
function assertExtPoint(other) { | ||
@@ -276,4 +284,4 @@ if (!(other instanceof ExtendedPoint)) | ||
static fromHex(hex, strict = true) { | ||
const { d, P, a } = CURVE; | ||
hex = ensureBytes(hex, fieldLen); | ||
const { d, a } = CURVE; | ||
hex = (0, utils_js_1.ensureBytes)(hex, fieldLen); | ||
// 1. First, interpret the string as an integer in little-endian | ||
@@ -287,4 +295,4 @@ // representation. Bit 255 of this number is the least significant | ||
normed[fieldLen - 1] = lastByte & ~0x80; | ||
const y = bytesToNumberLE(normed); | ||
if (strict && y >= P) | ||
const y = (0, utils_js_1.bytesToNumberLE)(normed); | ||
if (strict && y >= Fp.ORDER) | ||
throw new Error('Expected 0 < hex < P'); | ||
@@ -324,3 +332,3 @@ if (!strict && y >= maxGroupElement) | ||
toRawBytes() { | ||
const bytes = numberToBytesLE(this.y, fieldLen); | ||
const bytes = (0, utils_js_1.numberToBytesLE)(this.y, fieldLen); | ||
bytes[fieldLen - 1] |= this.x & _1n ? 0x80 : 0; | ||
@@ -331,3 +339,3 @@ return bytes; | ||
toHex() { | ||
return bytesToHex(this.toRawBytes()); | ||
return (0, utils_js_1.bytesToHex)(this.toRawBytes()); | ||
} | ||
@@ -379,5 +387,5 @@ isTorsionFree() { | ||
static fromHex(hex) { | ||
const bytes = ensureBytes(hex, 2 * fieldLen); | ||
const bytes = (0, utils_js_1.ensureBytes)(hex, 2 * fieldLen); | ||
const r = Point.fromHex(bytes.slice(0, fieldLen), false); | ||
const s = bytesToNumberLE(bytes.slice(fieldLen, 2 * fieldLen)); | ||
const s = (0, utils_js_1.bytesToNumberLE)(bytes.slice(fieldLen, 2 * fieldLen)); | ||
return new Signature(r, s); | ||
@@ -394,6 +402,6 @@ } | ||
toRawBytes() { | ||
return concatBytes(this.r.toRawBytes(), numberToBytesLE(this.s, fieldLen)); | ||
return (0, utils_js_1.concatBytes)(this.r.toRawBytes(), (0, utils_js_1.numberToBytesLE)(this.s, fieldLen)); | ||
} | ||
toHex() { | ||
return bytesToHex(this.toRawBytes()); | ||
return (0, utils_js_1.bytesToHex)(this.toRawBytes()); | ||
} | ||
@@ -403,3 +411,3 @@ } | ||
function modlLE(hash) { | ||
return mod.mod(bytesToNumberLE(hash), CURVE_ORDER); | ||
return mod.mod((0, utils_js_1.bytesToNumberLE)(hash), CURVE_ORDER); | ||
} | ||
@@ -433,4 +441,4 @@ /** | ||
typeof key === 'bigint' || typeof key === 'number' | ||
? numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen) | ||
: ensureBytes(key); | ||
? (0, utils_js_1.numberToBytesLE)(normalizeScalar(key, maxGroupElement), groupLen) | ||
: (0, utils_js_1.ensureBytes)(key); | ||
if (key.length !== groupLen) | ||
@@ -468,3 +476,3 @@ throw new Error(`Expected ${groupLen} bytes, got ${key.length}`); | ||
function hashDomainToScalar(message, context = EMPTY) { | ||
context = ensureBytes(context); | ||
context = (0, utils_js_1.ensureBytes)(context); | ||
return modlLE(CURVE.hash(domain(message, context, !!CURVE.preHash))); | ||
@@ -474,9 +482,9 @@ } | ||
function sign(message, privateKey, context) { | ||
message = ensureBytes(message); | ||
message = (0, utils_js_1.ensureBytes)(message); | ||
if (CURVE.preHash) | ||
message = CURVE.preHash(message); | ||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey); | ||
const r = hashDomainToScalar(concatBytes(prefix, message), context); | ||
const r = hashDomainToScalar((0, utils_js_1.concatBytes)(prefix, message), context); | ||
const R = Point.BASE.multiply(r); // R = rG | ||
const k = hashDomainToScalar(concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg) | ||
const k = hashDomainToScalar((0, utils_js_1.concatBytes)(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg) | ||
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp | ||
@@ -495,3 +503,3 @@ return new Signature(R, s).toRawBytes(); | ||
function verify(sig, message, publicKey, context) { | ||
message = ensureBytes(message); | ||
message = (0, utils_js_1.ensureBytes)(message); | ||
if (CURVE.preHash) | ||
@@ -521,3 +529,3 @@ message = CURVE.preHash(message); | ||
const SB = ExtendedPoint.BASE.multiplyUnsafe(s); | ||
const k = hashDomainToScalar(concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context); | ||
const k = hashDomainToScalar((0, utils_js_1.concatBytes)(r.toRawBytes(), publicKey.toRawBytes(), message), context); | ||
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k); | ||
@@ -533,7 +541,7 @@ const RkA = ExtendedPoint.fromAffine(r).add(kA); | ||
mod: modP, | ||
invert: (a, m = CURVE.P) => mod.invert(a, m), | ||
invert: Fp.invert, | ||
/** | ||
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare). | ||
*/ | ||
hashToPrivateScalar: (hash) => hashToPrivateScalar(hash, CURVE_ORDER, true), | ||
hashToPrivateScalar: (hash) => (0, utils_js_1.hashToPrivateScalar)(hash, CURVE_ORDER, true), | ||
/** | ||
@@ -568,1 +576,2 @@ * ed25519 private keys are uniform 32-bit strings. We do not need to check for | ||
} | ||
exports.twistedEdwards = twistedEdwards; |
@@ -0,1 +1,4 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.wNAF = void 0; | ||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
@@ -6,3 +9,3 @@ // Default group related functions | ||
// Not big, but pretty complex and it is easy to break stuff. To avoid too much copy paste | ||
export function wNAF(c, bits) { | ||
function wNAF(c, bits) { | ||
const constTimeNegate = (condition, item) => { | ||
@@ -13,4 +16,2 @@ const neg = item.negate(); | ||
const opts = (W) => { | ||
if (256 % W) | ||
throw new Error('Invalid precomputation window, must be power of 2'); | ||
const windows = Math.ceil(bits / W) + 1; // +1, because | ||
@@ -64,2 +65,4 @@ const windowSize = 2 ** (W - 1); // -1 because we skip zero | ||
wNAF(W, precomputes, n) { | ||
// TODO: maybe check that scalar is less than group order? wNAF will fail otherwise | ||
// But need to carefully remove other checks before wNAF. ORDER == bits here | ||
const { windows, windowSize } = opts(W); | ||
@@ -111,1 +114,2 @@ let p = c.ZERO; | ||
} | ||
exports.wNAF = wNAF; |
@@ -1,2 +0,1 @@ | ||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
export declare function mod(a: bigint, b: bigint): bigint; | ||
@@ -13,17 +12,2 @@ /** | ||
/** | ||
* Division over finite field. | ||
* `a/b mod p == a * invert(b) mod p` | ||
*/ | ||
export declare function div(numerator: bigint, denominator: bigint, modulo: bigint): bigint; | ||
/** | ||
* Takes a list of numbers, efficiently inverts all of them. | ||
* @param nums list of bigints | ||
* @param p modulo | ||
* @returns list of inverted bigints | ||
* @example | ||
* invertBatch([1n, 2n, 4n], 21n); | ||
* // => [1n, 11n, 16n] | ||
*/ | ||
export declare function invertBatch(nums: bigint[], modulo: bigint): bigint[]; | ||
/** | ||
* Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p). | ||
@@ -34,3 +18,3 @@ * * (a | p) ≡ 1 if a is a square (mod p) | ||
*/ | ||
export declare function legendre(num: bigint, P: bigint): bigint; | ||
export declare function legendre(num: bigint, fieldPrime: bigint): bigint; | ||
/** | ||
@@ -41,1 +25,37 @@ * Calculates square root of a number in a finite field. | ||
export declare const isNegativeLE: (num: bigint, modulo: bigint) => boolean; | ||
export interface Field<T> { | ||
ORDER: bigint; | ||
BYTES: number; | ||
BITS: number; | ||
MASK: bigint; | ||
ZERO: T; | ||
ONE: T; | ||
create: (num: T) => T; | ||
isValid: (num: T) => boolean; | ||
isZero: (num: T) => boolean; | ||
negate(num: T): T; | ||
invert(num: T): T; | ||
sqrt(num: T): T; | ||
square(num: T): T; | ||
equals(lhs: T, rhs: T): boolean; | ||
add(lhs: T, rhs: T): T; | ||
subtract(lhs: T, rhs: T): T; | ||
multiply(lhs: T, rhs: T | bigint): T; | ||
pow(lhs: T, power: bigint): T; | ||
div(lhs: T, rhs: T | bigint): T; | ||
addN(lhs: T, rhs: T): T; | ||
subtractN(lhs: T, rhs: T): T; | ||
multiplyN(lhs: T, rhs: T | bigint): T; | ||
squareN(num: T): T; | ||
isOdd?(num: T): boolean; | ||
legendre?(num: T): T; | ||
pow(lhs: T, power: bigint): T; | ||
invertBatch: (lst: T[]) => T[]; | ||
toBytes(num: T): Uint8Array; | ||
fromBytes(bytes: Uint8Array): T; | ||
} | ||
export declare function validateField<T>(field: Field<T>): void; | ||
export declare function FpPow<T>(f: Field<T>, num: T, power: bigint): T; | ||
export declare function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[]; | ||
export declare function FpDiv<T>(f: Field<T>, lhs: T, rhs: T | bigint): T; | ||
export declare function Fp(ORDER: bigint, bitLen?: number, isLE?: boolean, redef?: Partial<Field<bigint>>): Readonly<Field<bigint>>; |
@@ -0,2 +1,6 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Fp = exports.FpDiv = exports.FpInvertBatch = exports.FpPow = exports.validateField = exports.isNegativeLE = exports.sqrt = exports.legendre = exports.invert = exports.pow2 = exports.pow = exports.mod = void 0; | ||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
const utils = require("./utils.js"); | ||
// Utilities for modular arithmetics | ||
@@ -7,6 +11,7 @@ const _0n = BigInt(0); | ||
// Calculates a modulo b | ||
export function mod(a, b) { | ||
function mod(a, b) { | ||
const result = a % b; | ||
return result >= _0n ? result : b + result; | ||
} | ||
exports.mod = mod; | ||
/** | ||
@@ -18,3 +23,4 @@ * Efficiently exponentiate num to power and do modular division. | ||
*/ | ||
export function pow(num, power, modulo) { | ||
// TODO: use field version && remove | ||
function pow(num, power, modulo) { | ||
if (modulo <= _0n || power < _0n) | ||
@@ -33,4 +39,6 @@ throw new Error('Expected power/modulo > 0'); | ||
} | ||
exports.pow = pow; | ||
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4) | ||
export function pow2(x, power, modulo) { | ||
// TODO: Fp version? | ||
function pow2(x, power, modulo) { | ||
let res = x; | ||
@@ -43,4 +51,5 @@ while (power-- > _0n) { | ||
} | ||
exports.pow2 = pow2; | ||
// Inverses number over modulo | ||
export function invert(number, modulo) { | ||
function invert(number, modulo) { | ||
if (number === _0n || modulo <= _0n) { | ||
@@ -67,41 +76,4 @@ throw new Error(`invert: expected positive integers, got n=${number} mod=${modulo}`); | ||
} | ||
exports.invert = invert; | ||
/** | ||
* Division over finite field. | ||
* `a/b mod p == a * invert(b) mod p` | ||
*/ | ||
export function div(numerator, denominator, modulo) { | ||
const num = mod(numerator, modulo); | ||
const iden = invert(denominator, modulo); | ||
return mod(num * iden, modulo); | ||
} | ||
/** | ||
* Takes a list of numbers, efficiently inverts all of them. | ||
* @param nums list of bigints | ||
* @param p modulo | ||
* @returns list of inverted bigints | ||
* @example | ||
* invertBatch([1n, 2n, 4n], 21n); | ||
* // => [1n, 11n, 16n] | ||
*/ | ||
export function invertBatch(nums, modulo) { | ||
const scratch = new Array(nums.length); | ||
// Walk from first to last, multiply them by each other MOD p | ||
const lastMultiplied = nums.reduce((acc, num, i) => { | ||
if (num === _0n) | ||
return acc; | ||
scratch[i] = acc; | ||
return mod(acc * num, modulo); | ||
}, _1n); | ||
// Invert last element | ||
const inverted = invert(lastMultiplied, modulo); | ||
// Walk from last to first, multiply them by inverted each other MOD p | ||
nums.reduceRight((acc, num, i) => { | ||
if (num === _0n) | ||
return acc; | ||
scratch[i] = mod(acc * scratch[i], modulo); | ||
return mod(acc * num, modulo); | ||
}, inverted); | ||
return scratch; | ||
} | ||
/** | ||
* Calculates Legendre symbol (a | p), which denotes the value of a^((p-1)/2) (mod p). | ||
@@ -112,9 +84,11 @@ * * (a | p) ≡ 1 if a is a square (mod p) | ||
*/ | ||
export function legendre(num, P) { | ||
return pow(num, (P - _1n) / _2n, P); | ||
function legendre(num, fieldPrime) { | ||
return pow(num, (fieldPrime - _1n) / _2n, fieldPrime); | ||
} | ||
exports.legendre = legendre; | ||
/** | ||
* Calculates square root of a number in a finite field. | ||
*/ | ||
export function sqrt(number, modulo) { | ||
// TODO: rewrite as generic Fp function && remove bls versions | ||
function sqrt(number, modulo) { | ||
// prettier-ignore | ||
@@ -127,4 +101,13 @@ const _3n = BigInt(3), _4n = BigInt(4), _5n = BigInt(5), _8n = BigInt(8); | ||
// sqrt n = n^((P+1)/4) | ||
if (P % _4n === _3n) | ||
return pow(n, p1div4, P); | ||
if (P % _4n === _3n) { | ||
// Not all roots possible! | ||
// const ORDER = | ||
// 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaabn; | ||
// const NUM = 72057594037927816n; | ||
// TODO: fix sqrtMod in secp256k1 | ||
const root = pow(n, p1div4, P); | ||
if (mod(root * root, modulo) !== number) | ||
throw new Error('Cannot find square root'); | ||
return root; | ||
} | ||
// P ≡ 5 (mod 8) | ||
@@ -140,3 +123,2 @@ if (P % _8n === _5n) { | ||
// Other cases: Tonelli-Shanks algorithm | ||
// Check whether n is square | ||
if (legendre(n, P) !== _1n) | ||
@@ -171,10 +153,124 @@ throw new Error('Cannot find square root'); | ||
} | ||
exports.sqrt = sqrt; | ||
// 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. | ||
const isNegativeLE = (num, modulo) => (mod(num, modulo) & _1n) === _1n; | ||
exports.isNegativeLE = isNegativeLE; | ||
// prettier-ignore | ||
const FIELD_FIELDS = [ | ||
'create', 'isValid', 'isZero', 'negate', 'invert', 'sqrt', 'square', | ||
'equals', 'add', 'subtract', 'multiply', 'pow', 'div', | ||
'addN', 'subtractN', 'multiplyN', 'squareN' | ||
]; | ||
function validateField(field) { | ||
for (const i of ['ORDER', 'MASK']) { | ||
if (typeof field[i] !== 'bigint') | ||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`); | ||
} | ||
for (const i of ['BYTES', 'BITS']) { | ||
if (typeof field[i] !== 'number') | ||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`); | ||
} | ||
for (const i of FIELD_FIELDS) { | ||
if (typeof field[i] !== 'function') | ||
throw new Error(`Invalid field param ${i}=${field[i]} (${typeof field[i]})`); | ||
} | ||
} | ||
exports.validateField = validateField; | ||
// Generic field functions | ||
function FpPow(f, num, power) { | ||
// Should have same speed as pow for bigints | ||
// TODO: benchmark! | ||
if (power < _0n) | ||
throw new Error('Expected power > 0'); | ||
if (power === _0n) | ||
return f.ONE; | ||
if (power === _1n) | ||
return num; | ||
let p = f.ONE; | ||
let d = num; | ||
while (power > _0n) { | ||
if (power & _1n) | ||
p = f.multiply(p, d); | ||
d = f.square(d); | ||
power >>= 1n; | ||
} | ||
return p; | ||
} | ||
exports.FpPow = FpPow; | ||
function FpInvertBatch(f, nums) { | ||
const tmp = new Array(nums.length); | ||
// Walk from first to last, multiply them by each other MOD p | ||
const lastMultiplied = nums.reduce((acc, num, i) => { | ||
if (f.isZero(num)) | ||
return acc; | ||
tmp[i] = acc; | ||
return f.multiply(acc, num); | ||
}, f.ONE); | ||
// Invert last element | ||
const inverted = f.invert(lastMultiplied); | ||
// Walk from last to first, multiply them by inverted each other MOD p | ||
nums.reduceRight((acc, num, i) => { | ||
if (f.isZero(num)) | ||
return acc; | ||
tmp[i] = f.multiply(acc, tmp[i]); | ||
return f.multiply(acc, num); | ||
}, inverted); | ||
return tmp; | ||
} | ||
exports.FpInvertBatch = FpInvertBatch; | ||
function FpDiv(f, lhs, rhs) { | ||
return f.multiply(lhs, typeof rhs === 'bigint' ? invert(rhs, f.ORDER) : f.invert(rhs)); | ||
} | ||
exports.FpDiv = FpDiv; | ||
// NOTE: very fragile, always bench. Major performance points: | ||
// - NonNormalized ops | ||
// - Object.freeze | ||
// - same shape of object (don't add/remove keys) | ||
function Fp(ORDER, bitLen, isLE = false, redef = {}) { | ||
if (ORDER <= _0n) | ||
throw new Error(`Expected Fp ORDER > 0, got ${ORDER}`); | ||
const { nBitLength: BITS, nByteLength: BYTES } = utils.nLength(ORDER, bitLen); | ||
if (BYTES > 2048) | ||
throw new Error('Field lengths over 2048 bytes are not supported'); | ||
const sqrtP = (num) => sqrt(num, ORDER); | ||
const f = Object.freeze({ | ||
ORDER, | ||
BITS, | ||
BYTES, | ||
MASK: utils.bitMask(BITS), | ||
ZERO: _0n, | ||
ONE: _1n, | ||
create: (num) => mod(num, ORDER), | ||
isValid: (num) => { | ||
if (typeof num !== 'bigint') | ||
throw new Error(`Invalid field element: expected bigint, got ${typeof num}`); | ||
return _0n <= num && num < ORDER; | ||
}, | ||
isZero: (num) => num === _0n, | ||
isOdd: (num) => (num & _1n) === _1n, | ||
negate: (num) => mod(-num, ORDER), | ||
equals: (lhs, rhs) => lhs === rhs, | ||
square: (num) => mod(num * num, ORDER), | ||
add: (lhs, rhs) => mod(lhs + rhs, ORDER), | ||
subtract: (lhs, rhs) => mod(lhs - rhs, ORDER), | ||
multiply: (lhs, rhs) => mod(lhs * rhs, ORDER), | ||
pow: (num, power) => FpPow(f, num, power), | ||
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER), | ||
// Same as above, but doesn't normalize | ||
squareN: (num) => num * num, | ||
addN: (lhs, rhs) => lhs + rhs, | ||
subtractN: (lhs, rhs) => lhs - rhs, | ||
multiplyN: (lhs, rhs) => lhs * rhs, | ||
invert: (num) => invert(num, ORDER), | ||
sqrt: redef.sqrt || sqrtP, | ||
invertBatch: (lst) => FpInvertBatch(f, lst), | ||
toBytes: (num) => isLE ? utils.numberToBytesLE(num, BYTES) : utils.numberToBytesBE(num, BYTES), | ||
fromBytes: (bytes) => { | ||
if (bytes.length !== BYTES) | ||
throw new Error(`Fp.fromBytes: expected ${BYTES}, got ${bytes.length}`); | ||
return isLE ? utils.bytesToNumberLE(bytes) : utils.bytesToNumberBE(bytes); | ||
}, | ||
}); | ||
return Object.freeze(f); | ||
} | ||
exports.Fp = Fp; |
@@ -1,5 +0,6 @@ | ||
import * as mod from './modular.js'; | ||
import { ensureBytes, numberToBytesLE, bytesToNumberLE, | ||
// nLength, | ||
} from './utils.js'; | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.montgomery = void 0; | ||
const mod = require("./modular.js"); | ||
const utils_js_1 = require("./utils.js"); | ||
const _0n = BigInt(0); | ||
@@ -36,3 +37,3 @@ const _1n = BigInt(1); | ||
// Uses only one coordinate instead of two | ||
export function montgomery(curveDef) { | ||
function montgomery(curveDef) { | ||
const CURVE = validateOpts(curveDef); | ||
@@ -148,6 +149,6 @@ const { P } = CURVE; | ||
function encodeUCoordinate(u) { | ||
return numberToBytesLE(modP(u), montgomeryBytes); | ||
return (0, utils_js_1.numberToBytesLE)(modP(u), montgomeryBytes); | ||
} | ||
function decodeUCoordinate(uEnc) { | ||
const u = ensureBytes(uEnc, montgomeryBytes); | ||
const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes); | ||
// Section 5: When receiving such an array, implementations of X25519 | ||
@@ -158,9 +159,9 @@ // MUST mask the most significant bit in the final byte. | ||
u[fieldLen - 1] &= 127; // 0b0111_1111 | ||
return bytesToNumberLE(u); | ||
return (0, utils_js_1.bytesToNumberLE)(u); | ||
} | ||
function decodeScalar(n) { | ||
const bytes = ensureBytes(n); | ||
const bytes = (0, utils_js_1.ensureBytes)(n); | ||
if (bytes.length !== montgomeryBytes && bytes.length !== fieldLen) | ||
throw new Error(`Expected ${montgomeryBytes} or ${fieldLen} bytes, got ${bytes.length}`); | ||
return bytesToNumberLE(adjustScalarBytes(bytes)); | ||
return (0, utils_js_1.bytesToNumberLE)(adjustScalarBytes(bytes)); | ||
} | ||
@@ -193,1 +194,2 @@ // Multiply point u by scalar | ||
} | ||
exports.montgomery = montgomery; |
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import * as mod from './modular.js'; | ||
export declare type Hex = Uint8Array | string; | ||
export declare type PrivKey = Hex | bigint | number; | ||
export declare type BasicCurve = { | ||
P: bigint; | ||
export declare type CHash = { | ||
(message: Uint8Array | string): Uint8Array; | ||
blockLen: number; | ||
outputLen: number; | ||
create(): any; | ||
}; | ||
export declare type BasicCurve<T> = { | ||
Fp: mod.Field<T>; | ||
n: bigint; | ||
@@ -10,9 +17,12 @@ nBitLength?: number; | ||
h: bigint; | ||
Gx: bigint; | ||
Gy: bigint; | ||
hEff?: bigint; | ||
Gx: T; | ||
Gy: T; | ||
wrapPrivateKey?: boolean; | ||
allowInfinityPoint?: boolean; | ||
}; | ||
export declare function validateOpts<T extends BasicCurve>(curve: T): Readonly<{ | ||
export declare function validateOpts<FP, T>(curve: BasicCurve<FP> & T): Readonly<{ | ||
readonly nBitLength: number; | ||
readonly nByteLength: number; | ||
} & T>; | ||
} & BasicCurve<FP> & T>; | ||
export declare function bytesToHex(uint8a: Uint8Array): string; | ||
@@ -32,7 +42,15 @@ export declare function numberToHexUnpadded(num: number | bigint): string; | ||
}; | ||
/** | ||
* 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 | ||
*/ | ||
export declare function hashToPrivateScalar(hash: Hex, CURVE_ORDER: bigint, isLE?: boolean): bigint; | ||
export declare function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean; | ||
/** | ||
* Cryptographically secure PRNG | ||
*/ | ||
export declare function randomBytes(bytesLength?: number): Uint8Array; | ||
export declare function bitLen(n: bigint): number; | ||
export declare const bitGet: (n: bigint, pos: number) => bigint; | ||
export declare const bitSet: (n: bigint, pos: number, value: boolean) => bigint; | ||
export declare const bitMask: (n: number) => bigint; |
@@ -0,10 +1,19 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.bitMask = exports.bitSet = exports.bitGet = exports.bitLen = 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; | ||
/*! @noble/curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// The import here is via the package name. This is to ensure | ||
// that exports mapping/resolution does fall into place. | ||
import { crypto } from './crypto.js'; | ||
export function validateOpts(curve) { | ||
for (const i of ['P', 'n', 'h', 'Gx', 'Gy']) { | ||
const mod = require("./modular.js"); | ||
const _0n = BigInt(0); | ||
const _1n = BigInt(1); | ||
const _2n = BigInt(2); | ||
function validateOpts(curve) { | ||
mod.validateField(curve.Fp); | ||
for (const i of ['n', 'h']) { | ||
if (typeof curve[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
if (!curve.Fp.isValid(curve.Gx)) | ||
throw new Error('Invalid generator X coordinate Fp element'); | ||
if (!curve.Fp.isValid(curve.Gy)) | ||
throw new Error('Invalid generator Y coordinate Fp element'); | ||
for (const i of ['nBitLength', 'nByteLength']) { | ||
@@ -19,5 +28,5 @@ if (curve[i] === undefined) | ||
} | ||
import * as mod from './modular.js'; | ||
exports.validateOpts = validateOpts; | ||
const hexes = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')); | ||
export function bytesToHex(uint8a) { | ||
function bytesToHex(uint8a) { | ||
if (!(uint8a instanceof Uint8Array)) | ||
@@ -32,7 +41,9 @@ throw new Error('Expected Uint8Array'); | ||
} | ||
export function numberToHexUnpadded(num) { | ||
exports.bytesToHex = bytesToHex; | ||
function numberToHexUnpadded(num) { | ||
const hex = num.toString(16); | ||
return hex.length & 1 ? `0${hex}` : hex; | ||
} | ||
export function hexToNumber(hex) { | ||
exports.numberToHexUnpadded = numberToHexUnpadded; | ||
function hexToNumber(hex) { | ||
if (typeof hex !== 'string') { | ||
@@ -44,4 +55,5 @@ throw new TypeError('hexToNumber: expected string, got ' + typeof hex); | ||
} | ||
exports.hexToNumber = hexToNumber; | ||
// Caching slows it down 2-3x | ||
export function hexToBytes(hex) { | ||
function hexToBytes(hex) { | ||
if (typeof hex !== 'string') { | ||
@@ -63,7 +75,9 @@ throw new TypeError('hexToBytes: expected string, got ' + typeof hex); | ||
} | ||
exports.hexToBytes = hexToBytes; | ||
// Big Endian | ||
export function bytesToNumberBE(bytes) { | ||
function bytesToNumberBE(bytes) { | ||
return hexToNumber(bytesToHex(bytes)); | ||
} | ||
export function bytesToNumberLE(uint8a) { | ||
exports.bytesToNumberBE = bytesToNumberBE; | ||
function bytesToNumberLE(uint8a) { | ||
if (!(uint8a instanceof Uint8Array)) | ||
@@ -73,5 +87,8 @@ throw new Error('Expected Uint8Array'); | ||
} | ||
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) { | ||
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 | ||
@@ -84,4 +101,5 @@ // is instance of Uint8Array, and its slice() creates **mutable** copy | ||
} | ||
exports.ensureBytes = ensureBytes; | ||
// Copies several Uint8Arrays into one. | ||
export function concatBytes(...arrays) { | ||
function concatBytes(...arrays) { | ||
if (!arrays.every((b) => b instanceof Uint8Array)) | ||
@@ -100,4 +118,5 @@ throw new Error('Uint8Array list expected'); | ||
} | ||
exports.concatBytes = concatBytes; | ||
// CURVE.n lengths | ||
export function nLength(n, nBitLength) { | ||
function nLength(n, nBitLength) { | ||
// Bit size, byte size of CURVE.n | ||
@@ -108,2 +127,3 @@ const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length; | ||
} | ||
exports.nLength = nLength; | ||
/** | ||
@@ -117,4 +137,3 @@ * Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF | ||
*/ | ||
const _1n = BigInt(1); | ||
export function hashToPrivateScalar(hash, CURVE_ORDER, isLE = false) { | ||
function hashToPrivateScalar(hash, CURVE_ORDER, isLE = false) { | ||
hash = ensureBytes(hash); | ||
@@ -128,3 +147,4 @@ const orderLen = nLength(CURVE_ORDER).nByteLength; | ||
} | ||
export function equalBytes(b1, b2) { | ||
exports.hashToPrivateScalar = hashToPrivateScalar; | ||
function equalBytes(b1, b2) { | ||
// We don't care about timing attacks here | ||
@@ -138,15 +158,22 @@ if (b1.length !== b2.length) | ||
} | ||
/** | ||
* Cryptographically secure PRNG | ||
*/ | ||
export function randomBytes(bytesLength = 32) { | ||
if (crypto.web) { | ||
return crypto.web.getRandomValues(new Uint8Array(bytesLength)); | ||
} | ||
else if (crypto.node) { | ||
return new Uint8Array(crypto.node.randomBytes(bytesLength).buffer); | ||
} | ||
else { | ||
throw new Error("The environment doesn't have randomBytes function"); | ||
} | ||
exports.equalBytes = equalBytes; | ||
// Bit operations | ||
// Amount of bits inside bigint (Same as n.toString(2).length) | ||
function bitLen(n) { | ||
let len; | ||
for (len = 0; n > 0n; n >>= _1n, len += 1) | ||
; | ||
return len; | ||
} | ||
exports.bitLen = bitLen; | ||
// Gets single bit at position. NOTE: first bit position is 0 (same as arrays) | ||
// Same as !!+Array.from(n.toString(2)).reverse()[pos] | ||
const bitGet = (n, pos) => (n >> BigInt(pos)) & 1n; | ||
exports.bitGet = bitGet; | ||
// Sets single bit at position | ||
const bitSet = (n, pos, value) => n | ((value ? _1n : _0n) << BigInt(pos)); | ||
exports.bitSet = bitSet; | ||
// Return mask for N bits (Same as BigInt(`0b${Array(i).fill('1').join('')}`)) | ||
// Not using ** operator with bigints for old engines. | ||
const bitMask = (n) => (_2n << BigInt(n - 1)) - _1n; | ||
exports.bitMask = bitMask; |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import { BasicCurve, Hex, PrivKey, randomBytes as utilRandomBytes } from './utils.js'; | ||
import * as mod from './modular.js'; | ||
import { Hex, PrivKey } from './utils.js'; | ||
import * as utils from './utils.js'; | ||
import { htfOpts } from './hashToCurve.js'; | ||
import { Group, GroupConstructor } from './group.js'; | ||
export declare type CHash = { | ||
(message: Uint8Array | string): Uint8Array; | ||
blockLen: number; | ||
outputLen: number; | ||
create(): any; | ||
}; | ||
declare type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
@@ -20,33 +17,15 @@ declare type EndomorphismOpts = { | ||
}; | ||
export declare type CurveType = BasicCurve & { | ||
a: bigint; | ||
b: bigint; | ||
lowS?: boolean; | ||
hash: CHash; | ||
hmac: HmacFnSync; | ||
randomBytes?: (bytesLength?: number) => Uint8Array; | ||
truncateHash?: (hash: Uint8Array, truncateOnly?: boolean) => bigint; | ||
sqrtMod?: (n: bigint) => bigint; | ||
export declare type BasicCurve<T> = utils.BasicCurve<T> & { | ||
a: T; | ||
b: T; | ||
normalizePrivateKey?: (key: PrivKey) => PrivKey; | ||
endo?: EndomorphismOpts; | ||
isTorsionFree?: (c: JacobianConstructor<T>, point: JacobianPointType<T>) => boolean; | ||
clearCofactor?: (c: JacobianConstructor<T>, point: JacobianPointType<T>) => JacobianPointType<T>; | ||
htfDefaults?: htfOpts; | ||
mapToCurve?: (scalar: bigint[]) => { | ||
x: T; | ||
y: T; | ||
}; | ||
}; | ||
declare function validateOpts(curve: CurveType): Readonly<{ | ||
readonly nBitLength: number; | ||
readonly nByteLength: number; | ||
readonly P: bigint; | ||
readonly n: bigint; | ||
readonly h: bigint; | ||
readonly Gx: bigint; | ||
readonly Gy: bigint; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
lowS: boolean; | ||
readonly hash: CHash; | ||
readonly hmac: HmacFnSync; | ||
randomBytes: typeof utilRandomBytes; | ||
readonly truncateHash?: ((hash: Uint8Array, truncateOnly?: boolean | undefined) => bigint) | undefined; | ||
readonly sqrtMod?: ((n: bigint) => bigint) | undefined; | ||
readonly normalizePrivateKey?: ((key: PrivKey) => PrivKey) | undefined; | ||
readonly endo?: EndomorphismOpts | undefined; | ||
}>; | ||
declare type Entropy = Hex | true; | ||
@@ -78,2 +57,54 @@ declare type SignOpts = { | ||
*/ | ||
export interface JacobianPointType<T> extends Group<JacobianPointType<T>> { | ||
readonly x: T; | ||
readonly y: T; | ||
readonly z: T; | ||
multiply(scalar: number | bigint, affinePoint?: PointType<T>): JacobianPointType<T>; | ||
multiplyUnsafe(scalar: bigint): JacobianPointType<T>; | ||
toAffine(invZ?: T): PointType<T>; | ||
} | ||
export interface JacobianConstructor<T> extends GroupConstructor<JacobianPointType<T>> { | ||
new (x: T, y: T, z: T): JacobianPointType<T>; | ||
fromAffine(p: PointType<T>): JacobianPointType<T>; | ||
toAffineBatch(points: JacobianPointType<T>[]): PointType<T>[]; | ||
normalizeZ(points: JacobianPointType<T>[]): JacobianPointType<T>[]; | ||
} | ||
export interface PointType<T> extends Group<PointType<T>> { | ||
readonly x: T; | ||
readonly y: T; | ||
_setWindowSize(windowSize: number): void; | ||
hasEvenY(): boolean; | ||
toRawBytes(isCompressed?: boolean): Uint8Array; | ||
toHex(isCompressed?: boolean): string; | ||
assertValidity(): void; | ||
multiplyAndAddUnsafe(Q: PointType<T>, a: bigint, b: bigint): PointType<T> | undefined; | ||
} | ||
export interface PointConstructor<T> extends GroupConstructor<PointType<T>> { | ||
new (x: T, y: T): PointType<T>; | ||
fromHex(hex: Hex): PointType<T>; | ||
fromPrivateKey(privateKey: PrivKey): PointType<T>; | ||
hashToCurve(msg: Hex, options?: Partial<htfOpts>): PointType<T>; | ||
encodeToCurve(msg: Hex, options?: Partial<htfOpts>): PointType<T>; | ||
} | ||
export declare type CurvePointsType<T> = BasicCurve<T> & { | ||
fromBytes: (bytes: Uint8Array) => { | ||
x: T; | ||
y: T; | ||
}; | ||
toBytes: (c: PointConstructor<T>, point: PointType<T>, compressed: boolean) => Uint8Array; | ||
}; | ||
export declare type CurvePointsRes<T> = { | ||
Point: PointConstructor<T>; | ||
JacobianPoint: JacobianConstructor<T>; | ||
normalizePrivateKey: (key: PrivKey) => bigint; | ||
weierstrassEquation: (x: T) => T; | ||
isWithinCurveOrder: (num: bigint) => boolean; | ||
}; | ||
export declare function weierstrassPoints<T>(opts: CurvePointsType<T>): { | ||
Point: PointConstructor<T>; | ||
JacobianPoint: JacobianConstructor<T>; | ||
normalizePrivateKey: (key: PrivKey) => bigint; | ||
weierstrassEquation: (x: T) => T; | ||
isWithinCurveOrder: (num: bigint) => boolean; | ||
}; | ||
export interface SignatureType { | ||
@@ -87,3 +118,3 @@ readonly r: bigint; | ||
normalizeS(): SignatureType; | ||
recoverPublicKey(msgHash: Hex): PointType; | ||
recoverPublicKey(msgHash: Hex): PointType<bigint>; | ||
toDERRawBytes(isCompressed?: boolean): Uint8Array; | ||
@@ -99,32 +130,38 @@ toDERHex(isCompressed?: boolean): string; | ||
}; | ||
export interface JacobianPointType extends Group<JacobianPointType> { | ||
readonly x: bigint; | ||
readonly y: bigint; | ||
readonly z: bigint; | ||
multiply(scalar: number | bigint, affinePoint?: PointType): JacobianPointType; | ||
multiplyUnsafe(scalar: bigint): JacobianPointType; | ||
toAffine(invZ?: bigint): PointType; | ||
} | ||
export interface JacobianPointConstructor extends GroupConstructor<JacobianPointType> { | ||
new (x: bigint, y: bigint, z: bigint): JacobianPointType; | ||
fromAffine(p: PointType): JacobianPointType; | ||
toAffineBatch(points: JacobianPointType[]): PointType[]; | ||
normalizeZ(points: JacobianPointType[]): JacobianPointType[]; | ||
} | ||
export interface PointType extends Group<PointType> { | ||
readonly x: bigint; | ||
readonly y: bigint; | ||
_setWindowSize(windowSize: number): void; | ||
hasEvenY(): boolean; | ||
toRawBytes(isCompressed?: boolean): Uint8Array; | ||
toHex(isCompressed?: boolean): string; | ||
assertValidity(): void; | ||
multiplyAndAddUnsafe(Q: PointType, a: bigint, b: bigint): PointType | undefined; | ||
} | ||
export interface PointConstructor extends GroupConstructor<PointType> { | ||
new (x: bigint, y: bigint): PointType; | ||
fromHex(hex: Hex): PointType; | ||
fromPrivateKey(privateKey: PrivKey): PointType; | ||
} | ||
export declare type PubKey = Hex | PointType; | ||
export declare type PubKey = Hex | PointType<bigint>; | ||
export declare type CurveType = BasicCurve<bigint> & { | ||
lowS?: boolean; | ||
hash: utils.CHash; | ||
hmac: HmacFnSync; | ||
randomBytes: (bytesLength?: number) => Uint8Array; | ||
truncateHash?: (hash: Uint8Array, truncateOnly?: boolean) => bigint; | ||
}; | ||
declare function validateOpts(curve: CurveType): Readonly<{ | ||
readonly nBitLength: number; | ||
readonly nByteLength: number; | ||
readonly Fp: mod.Field<bigint>; | ||
readonly n: bigint; | ||
readonly h: bigint; | ||
readonly hEff?: bigint | undefined; | ||
readonly Gx: bigint; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: PrivKey) => PrivKey) | undefined; | ||
readonly endo?: EndomorphismOpts | undefined; | ||
readonly isTorsionFree?: ((c: JacobianConstructor<bigint>, point: JacobianPointType<bigint>) => boolean) | undefined; | ||
readonly clearCofactor?: ((c: JacobianConstructor<bigint>, point: JacobianPointType<bigint>) => JacobianPointType<bigint>) | undefined; | ||
readonly htfDefaults?: htfOpts | undefined; | ||
readonly mapToCurve?: ((scalar: bigint[]) => { | ||
x: bigint; | ||
y: bigint; | ||
}) | undefined; | ||
lowS: boolean; | ||
readonly hash: utils.CHash; | ||
readonly hmac: HmacFnSync; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
readonly truncateHash?: ((hash: Uint8Array, truncateOnly?: boolean | undefined) => bigint) | undefined; | ||
}>; | ||
export declare type CurveFn = { | ||
@@ -138,4 +175,4 @@ CURVE: ReturnType<typeof validateOpts>; | ||
}) => boolean; | ||
Point: PointConstructor; | ||
JacobianPoint: JacobianPointConstructor; | ||
Point: PointConstructor<bigint>; | ||
JacobianPoint: JacobianConstructor<bigint>; | ||
Signature: SignatureConstructor; | ||
@@ -148,3 +185,3 @@ utils: { | ||
_normalizePrivateKey: (key: PrivKey) => bigint; | ||
_normalizePublicKey: (publicKey: PubKey) => PointType; | ||
_normalizePublicKey: (publicKey: PubKey) => PointType<bigint>; | ||
_isWithinCurveOrder: (num: bigint) => boolean; | ||
@@ -151,0 +188,0 @@ _isValidFieldElement: (num: bigint) => boolean; |
@@ -0,3 +1,6 @@ | ||
"use strict"; | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Implementation of Short Weierstrass curve. The formula is: y² = x³ + ax + b | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.weierstrass = exports.weierstrassPoints = void 0; | ||
// TODO: sync vs async naming | ||
@@ -10,39 +13,7 @@ // TODO: default randomBytes | ||
// 4. DRBG supports outputLen bigger than outputLen of hmac | ||
import * as mod from './modular.js'; | ||
import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar, validateOpts as utilOpts, randomBytes as utilRandomBytes, } from './utils.js'; | ||
import { wNAF } from './group.js'; | ||
// Should be separate from overrides, since overrides can use information about curve (for example nBits) | ||
function validateOpts(curve) { | ||
const opts = utilOpts(curve); | ||
for (const i of ['a', 'b']) { | ||
if (typeof opts[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`); | ||
} | ||
for (const fn of ['hash', 'hmac']) { | ||
if (typeof opts[fn] !== 'function') | ||
throw new Error(`Invalid ${fn} function`); | ||
} | ||
for (const fn of ['randomBytes']) { | ||
if (opts[fn] === undefined) | ||
continue; // Optional | ||
if (typeof opts[fn] !== 'function') | ||
throw new Error(`Invalid ${fn} function`); | ||
} | ||
if (!Number.isSafeInteger(opts.hash.outputLen)) | ||
throw new Error('Invalid hash function'); | ||
const endo = opts.endo; | ||
if (endo) { | ||
if (opts.a !== _0n) { | ||
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0'); | ||
} | ||
if (typeof endo !== 'object' || | ||
typeof endo.beta !== 'bigint' || | ||
typeof endo.splitScalar !== 'function') { | ||
throw new Error('Expected endomorphism with beta: bigint and splitScalar: function'); | ||
} | ||
} | ||
// Set defaults | ||
return Object.freeze({ lowS: true, randomBytes: utilRandomBytes, ...opts }); | ||
} | ||
// TODO: convert bits to bytes aligned to 32 bits? (224 for example) | ||
const mod = require("./modular.js"); | ||
const utils_js_1 = require("./utils.js"); | ||
const utils = require("./utils.js"); | ||
const hashToCurve_js_1 = require("./hashToCurve.js"); | ||
const group_js_1 = require("./group.js"); | ||
// DER encoding utilities | ||
@@ -61,3 +32,3 @@ class DERError extends Error { | ||
if (data.length < 2 || data[0] !== 0x02) { | ||
throw new DERError(`Invalid signature integer tag: ${bytesToHex(data)}`); | ||
throw new DERError(`Invalid signature integer tag: ${(0, utils_js_1.bytesToHex)(data)}`); | ||
} | ||
@@ -73,7 +44,7 @@ const len = data[1]; | ||
} | ||
return { data: bytesToNumberBE(res), left: data.subarray(len + 2) }; | ||
return { data: (0, utils_js_1.bytesToNumberBE)(res), left: data.subarray(len + 2) }; | ||
} | ||
function parseDERSignature(data) { | ||
if (data.length < 2 || data[0] != 0x30) { | ||
throw new DERError(`Invalid signature tag: ${bytesToHex(data)}`); | ||
throw new DERError(`Invalid signature tag: ${(0, utils_js_1.bytesToHex)(data)}`); | ||
} | ||
@@ -86,3 +57,3 @@ if (data[1] !== data.length - 2) { | ||
if (rBytesLeft.length) { | ||
throw new DERError(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`); | ||
throw new DERError(`Invalid signature: left bytes after parsing: ${(0, utils_js_1.bytesToHex)(rBytesLeft)}`); | ||
} | ||
@@ -97,83 +68,51 @@ return { r, s }; | ||
const _8n = BigInt(8); | ||
/** | ||
* Minimal HMAC-DRBG (NIST 800-90) for signatures. | ||
* Used only for RFC6979, does not fully implement DRBG spec. | ||
*/ | ||
class HmacDrbg { | ||
constructor(hashLen, qByteLen, hmacFn) { | ||
this.hashLen = hashLen; | ||
this.qByteLen = qByteLen; | ||
this.hmacFn = hmacFn; | ||
if (typeof hashLen !== 'number' || hashLen < 2) | ||
throw new Error('hashLen must be a number'); | ||
if (typeof qByteLen !== 'number' || qByteLen < 2) | ||
throw new Error('qByteLen must be a number'); | ||
if (typeof hmacFn !== 'function') | ||
throw new Error('hmacFn must be a function'); | ||
// Step B, Step C: set hashLen to 8*ceil(hlen/8) | ||
this.v = new Uint8Array(hashLen).fill(1); | ||
this.k = new Uint8Array(hashLen).fill(0); | ||
this.counter = 0; | ||
function validatePointOpts(curve) { | ||
const opts = utils.validateOpts(curve); | ||
const Fp = opts.Fp; | ||
for (const i of ['a', 'b']) { | ||
if (!Fp.isValid(curve[i])) | ||
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`); | ||
} | ||
hmacSync(...values) { | ||
return this.hmacFn(this.k, ...values); | ||
for (const i of ['isTorsionFree', 'clearCofactor', 'mapToCurve']) { | ||
if (curve[i] === undefined) | ||
continue; // Optional | ||
if (typeof curve[i] !== 'function') | ||
throw new Error(`Invalid ${i} function`); | ||
} | ||
incr() { | ||
if (this.counter >= 1000) | ||
throw new Error('Tried 1,000 k values for sign(), all were invalid'); | ||
this.counter += 1; | ||
} | ||
reseedSync(seed = new Uint8Array()) { | ||
this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed); | ||
this.v = this.hmacSync(this.v); | ||
if (seed.length === 0) | ||
return; | ||
this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed); | ||
this.v = this.hmacSync(this.v); | ||
} | ||
// TODO: review | ||
generateSync() { | ||
this.incr(); | ||
let len = 0; | ||
const out = []; | ||
while (len < this.qByteLen) { | ||
this.v = this.hmacSync(this.v); | ||
const sl = this.v.slice(); | ||
out.push(sl); | ||
len += this.v.length; | ||
const endo = opts.endo; | ||
if (endo) { | ||
if (!Fp.equals(opts.a, Fp.ZERO)) { | ||
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0'); | ||
} | ||
return concatBytes(...out); | ||
if (typeof endo !== 'object' || | ||
typeof endo.beta !== 'bigint' || | ||
typeof endo.splitScalar !== 'function') { | ||
throw new Error('Expected endomorphism with beta: bigint and splitScalar: function'); | ||
} | ||
} | ||
if (typeof opts.fromBytes !== 'function') | ||
throw new Error('Invalid fromBytes function'); | ||
if (typeof opts.toBytes !== 'function') | ||
throw new Error('Invalid fromBytes function'); | ||
// Requires including hashToCurve file | ||
if (opts.htfDefaults !== undefined) | ||
(0, hashToCurve_js_1.validateHTFOpts)(opts.htfDefaults); | ||
// Set defaults | ||
return Object.freeze({ ...opts }); | ||
} | ||
// Use only input from curveOpts! | ||
export function weierstrass(curveDef) { | ||
const CURVE = validateOpts(curveDef); | ||
const CURVE_ORDER = CURVE.n; | ||
function weierstrassPoints(opts) { | ||
const CURVE = validatePointOpts(opts); | ||
const Fp = CURVE.Fp; | ||
// Lengths | ||
// All curves has same field / group length as for now, but it can be different for other curves | ||
const groupLen = CURVE.nByteLength; | ||
const fieldLen = CURVE.nByteLength; // 32 (length of one field element) | ||
if (fieldLen > 2048) | ||
throw new Error('Field lengths over 2048 are not supported'); | ||
const compressedLen = fieldLen + 1; // 33 | ||
const uncompressedLen = 2 * fieldLen + 1; // 65 | ||
const { nByteLength, nBitLength } = CURVE; | ||
const groupLen = nByteLength; | ||
// Not using ** operator with bigints for old engines. | ||
// 2n ** (8n * 32n) == 2n << (8n * 32n - 1n) | ||
const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n); | ||
function numToFieldStr(num) { | ||
if (typeof num !== 'bigint') | ||
throw new Error('Expected bigint'); | ||
if (!(_0n <= num && num < FIELD_MASK)) | ||
throw new Error(`Expected number < 2^${fieldLen * 8}`); | ||
return num.toString(16).padStart(2 * fieldLen, '0'); | ||
} | ||
function numToField(num) { | ||
const b = hexToBytes(numToFieldStr(num)); | ||
if (b.length !== fieldLen) | ||
throw new Error(`Error: expected ${fieldLen} bytes`); | ||
return b; | ||
} | ||
function modP(n, m = CURVE.P) { | ||
return mod.mod(n, m); | ||
} | ||
//const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n); | ||
// function numToFieldStr(num: bigint): string { | ||
// if (typeof num !== 'bigint') throw new Error('Expected bigint'); | ||
// if (!(_0n <= num && num < FIELD_MASK)) throw new Error(`Expected number < 2^${fieldLen * 8}`); | ||
// return num.toString(16).padStart(2 * fieldLen, '0'); | ||
// } | ||
/** | ||
@@ -185,5 +124,5 @@ * y² = x³ + ax + b: Short weierstrass curve formula | ||
const { a, b } = CURVE; | ||
const x2 = modP(x * x); | ||
const x3 = modP(x2 * x); | ||
return modP(x3 + a * x + b); | ||
const x2 = Fp.square(x); // x * x | ||
const x3 = Fp.multiply(x2, x); // x2 * x | ||
return Fp.add(Fp.add(x3, Fp.multiply(x, a)), b); // x3 + a * x + b | ||
} | ||
@@ -193,10 +132,7 @@ function isWithinCurveOrder(num) { | ||
} | ||
function isValidFieldElement(num) { | ||
return _0n < num && num < CURVE.P; | ||
} | ||
function normalizePrivateKey(key) { | ||
let num; | ||
if (typeof CURVE.normalizePrivateKey === 'function') { | ||
key = CURVE.normalizePrivateKey(key); | ||
} | ||
let num; | ||
if (typeof key === 'bigint') { | ||
@@ -211,3 +147,3 @@ num = key; | ||
throw new Error(`Expected ${groupLen} bytes of private key`); | ||
num = hexToNumber(key); | ||
num = (0, utils_js_1.hexToNumber)(key); | ||
} | ||
@@ -217,3 +153,3 @@ else if (key instanceof Uint8Array) { | ||
throw new Error(`Expected ${groupLen} bytes of private key`); | ||
num = bytesToNumberBE(key); | ||
num = (0, utils_js_1.bytesToNumberBE)(key); | ||
} | ||
@@ -223,2 +159,4 @@ else { | ||
} | ||
if (CURVE.wrapPrivateKey) | ||
num = mod.mod(num, CURVE.n); | ||
if (!isWithinCurveOrder(num)) | ||
@@ -228,24 +166,2 @@ throw new Error('Expected private key: 0 < key < n'); | ||
} | ||
/** | ||
* Normalizes hex, bytes, Point to Point. Checks for curve equation. | ||
*/ | ||
function normalizePublicKey(publicKey) { | ||
if (publicKey instanceof Point) { | ||
publicKey.assertValidity(); | ||
return publicKey; | ||
} | ||
else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') { | ||
return Point.fromHex(publicKey); | ||
// This can happen because PointType can be instance of different class | ||
} | ||
else | ||
throw new Error(`Unknown type of public key: ${publicKey}`); | ||
} | ||
function isBiggerThanHalfOrder(number) { | ||
const HALF = CURVE_ORDER >> _1n; | ||
return number > HALF; | ||
} | ||
function normalizeS(s) { | ||
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s; | ||
} | ||
function normalizeScalar(num) { | ||
@@ -258,16 +174,2 @@ if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0) | ||
} | ||
const sqrtModCurve = CURVE.sqrtMod || mod.sqrt; | ||
// Ensures ECDSA message hashes are 32 bytes and < curve order | ||
function _truncateHash(hash, truncateOnly = false) { | ||
const { n, nBitLength } = CURVE; | ||
const byteLength = hash.length; | ||
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits) | ||
let h = bytesToNumberBE(hash); | ||
if (delta > 0) | ||
h = h >> BigInt(delta); | ||
if (!truncateOnly && h >= n) | ||
h -= n; | ||
return h; | ||
} | ||
const truncateHash = CURVE.truncateHash || _truncateHash; | ||
/** | ||
@@ -291,3 +193,3 @@ * Jacobian Point works in 3d / jacobi coordinates: (x, y, z) ∋ (x=x/z², y=y/z³) | ||
return JacobianPoint.ZERO; | ||
return new JacobianPoint(p.x, p.y, _1n); | ||
return new JacobianPoint(p.x, p.y, Fp.ONE); | ||
} | ||
@@ -300,3 +202,3 @@ /** | ||
static toAffineBatch(points) { | ||
const toInv = mod.invertBatch(points.map((p) => p.z), CURVE.P); | ||
const toInv = Fp.invertBatch(points.map((p) => p.z)); | ||
return points.map((p, i) => p.toAffine(toInv[i])); | ||
@@ -315,9 +217,9 @@ } | ||
const { x: X2, y: Y2, z: Z2 } = other; | ||
const Z1Z1 = modP(Z1 * Z1); | ||
const Z2Z2 = modP(Z2 * Z2); | ||
const U1 = modP(X1 * Z2Z2); | ||
const U2 = modP(X2 * Z1Z1); | ||
const S1 = modP(modP(Y1 * Z2) * Z2Z2); | ||
const S2 = modP(modP(Y2 * Z1) * Z1Z1); | ||
return U1 === U2 && S1 === S2; | ||
const Z1Z1 = Fp.square(Z1); // Z1 * Z1 | ||
const Z2Z2 = Fp.square(Z2); // Z2 * Z2 | ||
const U1 = Fp.multiply(X1, Z2Z2); // X1 * Z2Z2 | ||
const U2 = Fp.multiply(X2, Z1Z1); // X2 * Z1Z1 | ||
const S1 = Fp.multiply(Fp.multiply(Y1, Z2), Z2Z2); // Y1 * Z2 * Z2Z2 | ||
const S2 = Fp.multiply(Fp.multiply(Y2, Z1), Z1Z1); // Y2 * Z1 * Z1Z1 | ||
return Fp.equals(U1, U2) && Fp.equals(S1, S2); | ||
} | ||
@@ -328,3 +230,3 @@ /** | ||
negate() { | ||
return new JacobianPoint(this.x, modP(-this.y), this.z); | ||
return new JacobianPoint(this.x, Fp.negate(this.y), this.z); | ||
} | ||
@@ -340,27 +242,27 @@ // Fast algo for doubling 2 Jacobian Points. | ||
// Cost: 2M + 5S + 6add + 3*2 + 1*3 + 1*8. | ||
if (a === _0n) { | ||
const A = modP(X1 * X1); | ||
const B = modP(Y1 * Y1); | ||
const C = modP(B * B); | ||
const x1b = X1 + B; | ||
const D = modP(_2n * (modP(x1b * x1b) - A - C)); | ||
const E = modP(_3n * A); | ||
const F = modP(E * E); | ||
const X3 = modP(F - _2n * D); | ||
const Y3 = modP(E * (D - X3) - _8n * C); | ||
const Z3 = modP(_2n * Y1 * Z1); | ||
if (Fp.isZero(a)) { | ||
const A = Fp.square(X1); // X1 * X1 | ||
const B = Fp.square(Y1); // Y1 * Y1 | ||
const C = Fp.square(B); // B * B | ||
const x1b = Fp.addN(X1, B); // X1 + B | ||
const D = Fp.multiply(Fp.subtractN(Fp.subtractN(Fp.square(x1b), A), C), _2n); // ((x1b * x1b) - A - C) * 2 | ||
const E = Fp.multiply(A, _3n); // A * 3 | ||
const F = Fp.square(E); // E * E | ||
const X3 = Fp.subtract(F, Fp.multiplyN(D, _2n)); // F - 2 * D | ||
const Y3 = Fp.subtract(Fp.multiplyN(E, Fp.subtractN(D, X3)), Fp.multiplyN(C, _8n)); // E * (D - X3) - 8 * C; | ||
const Z3 = Fp.multiply(Fp.multiplyN(Y1, _2n), Z1); // 2 * Y1 * Z1 | ||
return new JacobianPoint(X3, Y3, Z3); | ||
} | ||
const XX = modP(X1 * X1); | ||
const YY = modP(Y1 * Y1); | ||
const YYYY = modP(YY * YY); | ||
const ZZ = modP(Z1 * Z1); | ||
const tmp1 = modP(X1 + YY); | ||
const S = modP(_2n * (modP(tmp1 * tmp1) - XX - YYYY)); // 2*((X1+YY)^2-XX-YYYY) | ||
const M = modP(_3n * XX + a * modP(ZZ * ZZ)); | ||
const T = modP(modP(M * M) - _2n * S); // M^2-2*S | ||
const XX = Fp.square(X1); // X1 * X1 | ||
const YY = Fp.square(Y1); // Y1 * Y1 | ||
const YYYY = Fp.square(YY); // YY * YY | ||
const ZZ = Fp.square(Z1); // Z1 * Z1 | ||
const tmp1 = Fp.add(X1, YY); // X1 + YY | ||
const S = Fp.multiply(Fp.subtractN(Fp.subtractN(Fp.square(tmp1), XX), YYYY), _2n); // 2*((X1+YY)^2-XX-YYYY) | ||
const M = Fp.add(Fp.multiplyN(XX, _3n), Fp.multiplyN(Fp.square(ZZ), a)); // 3 * XX + a * ZZ^2 | ||
const T = Fp.subtract(Fp.square(M), Fp.multiplyN(S, _2n)); // M^2-2*S | ||
const X3 = T; | ||
const Y3 = modP(M * (S - T) - _8n * YYYY); // M*(S-T)-8*YYYY | ||
const y1az1 = modP(Y1 + Z1); // (Y1+Z1) | ||
const Z3 = modP(modP(y1az1 * y1az1) - YY - ZZ); // (Y1+Z1)^2-YY-ZZ | ||
const Y3 = Fp.subtract(Fp.multiplyN(M, Fp.subtractN(S, T)), Fp.multiplyN(YYYY, _8n)); // M*(S-T)-8*YYYY | ||
const y1az1 = Fp.add(Y1, Z1); // (Y1+Z1) | ||
const Z3 = Fp.subtract(Fp.subtractN(Fp.square(y1az1), YY), ZZ); // (Y1+Z1)^2-YY-ZZ | ||
return new JacobianPoint(X3, Y3, Z3); | ||
@@ -377,24 +279,24 @@ } | ||
const { x: X2, y: Y2, z: Z2 } = other; | ||
if (X2 === _0n || Y2 === _0n) | ||
if (Fp.isZero(X2) || Fp.isZero(Y2)) | ||
return this; | ||
if (X1 === _0n || Y1 === _0n) | ||
if (Fp.isZero(X1) || Fp.isZero(Y1)) | ||
return other; | ||
// We're using same code in equals() | ||
const Z1Z1 = modP(Z1 * Z1); // Z1Z1 = Z1^2 | ||
const Z2Z2 = modP(Z2 * Z2); // Z2Z2 = Z2^2; | ||
const U1 = modP(X1 * Z2Z2); // X1 * Z2Z2 | ||
const U2 = modP(X2 * Z1Z1); // X2 * Z1Z1 | ||
const S1 = modP(modP(Y1 * Z2) * Z2Z2); // Y1 * Z2 * Z2Z2 | ||
const S2 = modP(modP(Y2 * Z1) * Z1Z1); // Y2 * Z1 * Z1Z1 | ||
const H = modP(U2 - U1); // H = U2 - U1 | ||
const r = modP(S2 - S1); // S2 - S1 | ||
const Z1Z1 = Fp.square(Z1); // Z1Z1 = Z1^2 | ||
const Z2Z2 = Fp.square(Z2); // Z2Z2 = Z2^2; | ||
const U1 = Fp.multiply(X1, Z2Z2); // X1 * Z2Z2 | ||
const U2 = Fp.multiply(X2, Z1Z1); // X2 * Z1Z1 | ||
const S1 = Fp.multiply(Fp.multiply(Y1, Z2), Z2Z2); // Y1 * Z2 * Z2Z2 | ||
const S2 = Fp.multiply(Fp.multiply(Y2, Z1), Z1Z1); // Y2 * Z1 * Z1Z1 | ||
const H = Fp.subtractN(U2, U1); // H = U2 - U1 | ||
const r = Fp.subtractN(S2, S1); // S2 - S1 | ||
// H = 0 meaning it's the same point. | ||
if (H === _0n) | ||
return r === _0n ? this.double() : JacobianPoint.ZERO; | ||
const HH = modP(H * H); // HH = H2 | ||
const HHH = modP(H * HH); // HHH = H * HH | ||
const V = modP(U1 * HH); // V = U1 * HH | ||
const X3 = modP(r * r - HHH - _2n * V); // X3 = r^2 - HHH - 2 * V; | ||
const Y3 = modP(r * (V - X3) - S1 * HHH); // Y3 = r * (V - X3) - S1 * HHH; | ||
const Z3 = modP(Z1 * Z2 * H); // Z3 = Z1 * Z2 * H; | ||
if (Fp.isZero(H)) | ||
return Fp.isZero(r) ? this.double() : JacobianPoint.ZERO; | ||
const HH = Fp.square(H); // HH = H2 | ||
const HHH = Fp.multiply(H, HH); // HHH = H * HH | ||
const V = Fp.multiply(U1, HH); // V = U1 * HH | ||
const X3 = Fp.subtract(Fp.subtractN(Fp.squareN(r), HHH), Fp.multiplyN(V, _2n)); // X3 = r^2 - HHH - 2 * V; | ||
const Y3 = Fp.subtract(Fp.multiplyN(r, Fp.subtractN(V, X3)), Fp.multiplyN(S1, HHH)); // Y3 = r * (V - X3) - S1 * HHH; | ||
const Z3 = Fp.multiply(Fp.multiply(Z1, Z2), H); // Z3 = Z1 * Z2 * H; | ||
return new JacobianPoint(X3, Y3, Z3); | ||
@@ -438,3 +340,3 @@ } | ||
k2p = k2p.negate(); | ||
k2p = new JacobianPoint(modP(k2p.x * CURVE.endo.beta), k2p.y, k2p.z); | ||
k2p = new JacobianPoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z); | ||
return k1p.add(k2p); | ||
@@ -480,3 +382,3 @@ } | ||
k2p = wnaf.constTimeNegate(k2neg, k2p); | ||
k2p = new JacobianPoint(modP(k2p.x * CURVE.endo.beta), k2p.y, k2p.z); | ||
k2p = new JacobianPoint(Fp.multiply(k2p.x, CURVE.endo.beta), k2p.y, k2p.z); | ||
point = k1p.add(k2p); | ||
@@ -501,21 +403,38 @@ fake = f1p.add(f2p); | ||
// If invZ was 0, we return zero point. However we still want to execute | ||
// all operations, so we replace invZ with a random number, 8. | ||
// all operations, so we replace invZ with a random number, 1. | ||
if (invZ == null) | ||
invZ = is0 ? _8n : mod.invert(z, CURVE.P); | ||
invZ = is0 ? Fp.ONE : Fp.invert(z); | ||
const iz1 = invZ; | ||
const iz2 = modP(iz1 * iz1); | ||
const iz3 = modP(iz2 * iz1); | ||
const ax = modP(x * iz2); | ||
const ay = modP(y * iz3); | ||
const zz = modP(z * iz1); | ||
const iz2 = Fp.square(iz1); // iz1 * iz1 | ||
const iz3 = Fp.multiply(iz2, iz1); // iz2 * iz1 | ||
const ax = Fp.multiply(x, iz2); // x * iz2 | ||
const ay = Fp.multiply(y, iz3); // y * iz3 | ||
const zz = Fp.multiply(z, iz1); // z * iz1 | ||
if (is0) | ||
return Point.ZERO; | ||
if (zz !== _1n) | ||
if (!Fp.equals(zz, Fp.ONE)) | ||
throw new Error('invZ was invalid'); | ||
return new Point(ax, ay); | ||
} | ||
isTorsionFree() { | ||
if (CURVE.h === _1n) | ||
return true; // No subgroups, always torsion fee | ||
if (CURVE.isTorsionFree) | ||
return CURVE.isTorsionFree(JacobianPoint, this); | ||
// is multiplyUnsafe(CURVE.n) is always ok, same as for edwards? | ||
throw new Error('Unsupported!'); | ||
} | ||
// Clear cofactor of G1 | ||
// https://eprint.iacr.org/2019/403 | ||
clearCofactor() { | ||
if (CURVE.h === _1n) | ||
return this; // Fast-path | ||
if (CURVE.clearCofactor) | ||
return CURVE.clearCofactor(JacobianPoint, this); | ||
return this.multiplyUnsafe(CURVE.h); | ||
} | ||
} | ||
JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, _1n); | ||
JacobianPoint.ZERO = new JacobianPoint(_0n, _1n, _0n); | ||
const wnaf = wNAF(JacobianPoint, CURVE.endo ? CURVE.nBitLength / 2 : CURVE.nBitLength); | ||
JacobianPoint.BASE = new JacobianPoint(CURVE.Gx, CURVE.Gy, Fp.ONE); | ||
JacobianPoint.ZERO = new JacobianPoint(Fp.ZERO, Fp.ONE, Fp.ZERO); | ||
const wnaf = (0, group_js_1.wNAF)(JacobianPoint, CURVE.endo ? nBitLength / 2 : nBitLength); | ||
// Stores precomputed values for points. | ||
@@ -538,20 +457,12 @@ const pointPrecomputes = new WeakMap(); | ||
hasEvenY() { | ||
return this.y % _2n === _0n; | ||
if (Fp.isOdd) | ||
return !Fp.isOdd(this.y); | ||
throw new Error("Field doesn't support isOdd"); | ||
} | ||
/** | ||
* Supports compressed ECDSA points | ||
* @returns Point instance | ||
* Converts hash string or Uint8Array to Point. | ||
* @param hex short/long ECDSA hex | ||
*/ | ||
static fromCompressedHex(bytes) { | ||
const P = CURVE.P; | ||
const x = bytesToNumberBE(bytes.subarray(1)); | ||
if (!isValidFieldElement(x)) | ||
throw new Error('Point is not on curve'); | ||
const y2 = weierstrassEquation(x); // y² = x³ + ax + b | ||
let y = sqrtModCurve(y2, P); // y = y² ^ (p+1)/4 | ||
const isYOdd = (y & _1n) === _1n; | ||
// ECDSA | ||
const isFirstByteOdd = (bytes[0] & 1) === 1; | ||
if (isFirstByteOdd !== isYOdd) | ||
y = modP(-y); | ||
static fromHex(hex) { | ||
const { x, y } = CURVE.fromBytes((0, utils_js_1.ensureBytes)(hex)); | ||
const point = new Point(x, y); | ||
@@ -561,24 +472,2 @@ point.assertValidity(); | ||
} | ||
static fromUncompressedHex(bytes) { | ||
const x = bytesToNumberBE(bytes.subarray(1, fieldLen + 1)); | ||
const y = bytesToNumberBE(bytes.subarray(fieldLen + 1, 2 * fieldLen + 1)); | ||
const point = new Point(x, y); | ||
point.assertValidity(); | ||
return point; | ||
} | ||
/** | ||
* Converts hash string or Uint8Array to Point. | ||
* @param hex short/long ECDSA hex | ||
*/ | ||
static fromHex(hex) { | ||
const bytes = ensureBytes(hex); | ||
const len = bytes.length; | ||
const header = bytes[0]; | ||
// this.assertValidity() is done inside of those two functions | ||
if (len === compressedLen && (header === 0x02 || header === 0x03)) | ||
return this.fromCompressedHex(bytes); | ||
if (len === uncompressedLen && header === 0x04) | ||
return this.fromUncompressedHex(bytes); | ||
throw new Error(`Point.fromHex: received invalid point. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`); | ||
} | ||
// Multiplies generator point by privateKey. | ||
@@ -589,25 +478,28 @@ static fromPrivateKey(privateKey) { | ||
toRawBytes(isCompressed = false) { | ||
return hexToBytes(this.toHex(isCompressed)); | ||
this.assertValidity(); | ||
return CURVE.toBytes(Point, this, isCompressed); | ||
} | ||
toHex(isCompressed = false) { | ||
const x = numToFieldStr(this.x); | ||
if (isCompressed) { | ||
const prefix = this.hasEvenY() ? '02' : '03'; | ||
return `${prefix}${x}`; | ||
} | ||
else { | ||
return `04${x}${numToFieldStr(this.y)}`; | ||
} | ||
return (0, utils_js_1.bytesToHex)(this.toRawBytes(isCompressed)); | ||
} | ||
// A point on curve is valid if it conforms to equation. | ||
assertValidity() { | ||
// Zero is valid point too! | ||
if (this.equals(Point.ZERO)) { | ||
if (CURVE.allowInfinityPoint) | ||
return; | ||
throw new Error('Point is infinity'); | ||
} | ||
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex` | ||
const msg = 'Point is not on elliptic curve'; | ||
const { x, y } = this; | ||
if (!isValidFieldElement(x) || !isValidFieldElement(y)) | ||
if (!Fp.isValid(x) || !Fp.isValid(y)) | ||
throw new Error(msg); | ||
const left = modP(y * y); | ||
const left = Fp.square(y); | ||
const right = weierstrassEquation(x); | ||
if (modP(left - right) !== _0n) | ||
if (!Fp.equals(left, right)) | ||
throw new Error(msg); | ||
// TODO: flag to disable this? | ||
if (!this.isTorsionFree()) | ||
throw new Error('Point must be of prime-order subgroup'); | ||
} | ||
@@ -617,7 +509,7 @@ equals(other) { | ||
throw new TypeError('Point#equals: expected Point'); | ||
return this.x === other.x && this.y === other.y; | ||
return Fp.equals(this.x, other.x) && Fp.equals(this.y, other.y); | ||
} | ||
// Returns the same point with inverted `y` | ||
negate() { | ||
return new Point(this.x, modP(-this.y)); | ||
return new Point(this.x, Fp.negate(this.y)); | ||
} | ||
@@ -639,2 +531,11 @@ // Adds point to itself | ||
} | ||
multiplyUnsafe(scalar) { | ||
return JacobianPoint.fromAffine(this).multiplyUnsafe(scalar).toAffine(); | ||
} | ||
clearCofactor() { | ||
return JacobianPoint.fromAffine(this).clearCofactor().toAffine(); | ||
} | ||
isTorsionFree() { | ||
return JacobianPoint.fromAffine(this).isTorsionFree(); | ||
} | ||
/** | ||
@@ -653,2 +554,23 @@ * Efficiently calculate `aP + bQ`. | ||
} | ||
// Encodes byte string to elliptic curve | ||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3 | ||
static hashToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = (0, utils_js_1.ensureBytes)(msg); | ||
const u = (0, hashToCurve_js_1.hash_to_field)(msg, 2, { ...CURVE.htfDefaults, ...options }); | ||
const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]); | ||
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]); | ||
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor(); | ||
return p; | ||
} | ||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3 | ||
static encodeToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = (0, utils_js_1.ensureBytes)(msg); | ||
const u = (0, hashToCurve_js_1.hash_to_field)(msg, 1, { ...CURVE.htfDefaults, ...options }); | ||
const { x, y } = CURVE.mapToCurve(u[0]); | ||
return new Point(x, y).clearCofactor(); | ||
} | ||
} | ||
@@ -662,4 +584,165 @@ /** | ||
*/ | ||
Point.ZERO = new Point(_0n, _0n); | ||
Point.ZERO = new Point(Fp.ZERO, Fp.ZERO); | ||
return { | ||
Point: Point, | ||
JacobianPoint: JacobianPoint, | ||
normalizePrivateKey, | ||
weierstrassEquation, | ||
isWithinCurveOrder, | ||
}; | ||
} | ||
exports.weierstrassPoints = weierstrassPoints; | ||
function validateOpts(curve) { | ||
const opts = utils.validateOpts(curve); | ||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen)) | ||
throw new Error('Invalid hash function'); | ||
if (typeof opts.hmac !== 'function') | ||
throw new Error('Invalid hmac function'); | ||
if (typeof opts.randomBytes !== 'function') | ||
throw new Error('Invalid randomBytes function'); | ||
// Set defaults | ||
return Object.freeze({ lowS: true, ...opts }); | ||
} | ||
/** | ||
* Minimal HMAC-DRBG (NIST 800-90) for signatures. | ||
* Used only for RFC6979, does not fully implement DRBG spec. | ||
*/ | ||
class HmacDrbg { | ||
constructor(hashLen, qByteLen, hmacFn) { | ||
this.hashLen = hashLen; | ||
this.qByteLen = qByteLen; | ||
this.hmacFn = hmacFn; | ||
if (typeof hashLen !== 'number' || hashLen < 2) | ||
throw new Error('hashLen must be a number'); | ||
if (typeof qByteLen !== 'number' || qByteLen < 2) | ||
throw new Error('qByteLen must be a number'); | ||
if (typeof hmacFn !== 'function') | ||
throw new Error('hmacFn must be a function'); | ||
// Step B, Step C: set hashLen to 8*ceil(hlen/8) | ||
this.v = new Uint8Array(hashLen).fill(1); | ||
this.k = new Uint8Array(hashLen).fill(0); | ||
this.counter = 0; | ||
} | ||
hmacSync(...values) { | ||
return this.hmacFn(this.k, ...values); | ||
} | ||
incr() { | ||
if (this.counter >= 1000) | ||
throw new Error('Tried 1,000 k values for sign(), all were invalid'); | ||
this.counter += 1; | ||
} | ||
reseedSync(seed = new Uint8Array()) { | ||
this.k = this.hmacSync(this.v, Uint8Array.from([0x00]), seed); | ||
this.v = this.hmacSync(this.v); | ||
if (seed.length === 0) | ||
return; | ||
this.k = this.hmacSync(this.v, Uint8Array.from([0x01]), seed); | ||
this.v = this.hmacSync(this.v); | ||
} | ||
// TODO: review | ||
generateSync() { | ||
this.incr(); | ||
let len = 0; | ||
const out = []; | ||
while (len < this.qByteLen) { | ||
this.v = this.hmacSync(this.v); | ||
const sl = this.v.slice(); | ||
out.push(sl); | ||
len += this.v.length; | ||
} | ||
return (0, utils_js_1.concatBytes)(...out); | ||
} | ||
} | ||
function weierstrass(curveDef) { | ||
const CURVE = validateOpts(curveDef); | ||
const CURVE_ORDER = CURVE.n; | ||
const Fp = CURVE.Fp; | ||
const compressedLen = Fp.BYTES + 1; // 33 | ||
const uncompressedLen = 2 * Fp.BYTES + 1; // 65 | ||
function isValidFieldElement(num) { | ||
// 0 is disallowed by arbitrary reasons. Probably because infinity point? | ||
return _0n < num && num < Fp.ORDER; | ||
} | ||
const { Point, JacobianPoint, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder } = weierstrassPoints({ | ||
...CURVE, | ||
toBytes(c, point, isCompressed) { | ||
if (isCompressed) { | ||
return (0, utils_js_1.concatBytes)(new Uint8Array([point.hasEvenY() ? 0x02 : 0x03]), Fp.toBytes(point.x)); | ||
} | ||
else { | ||
return (0, utils_js_1.concatBytes)(new Uint8Array([0x04]), Fp.toBytes(point.x), Fp.toBytes(point.y)); | ||
} | ||
}, | ||
fromBytes(bytes) { | ||
const len = bytes.length; | ||
const header = bytes[0]; | ||
// this.assertValidity() is done inside of fromHex | ||
if (len === compressedLen && (header === 0x02 || header === 0x03)) { | ||
const x = (0, utils_js_1.bytesToNumberBE)(bytes.subarray(1)); | ||
if (!isValidFieldElement(x)) | ||
throw new Error('Point is not on curve'); | ||
const y2 = weierstrassEquation(x); // y² = x³ + ax + b | ||
let y = Fp.sqrt(y2); // y = y² ^ (p+1)/4 | ||
const isYOdd = (y & _1n) === _1n; | ||
// ECDSA | ||
const isFirstByteOdd = (bytes[0] & 1) === 1; | ||
if (isFirstByteOdd !== isYOdd) | ||
y = Fp.negate(y); | ||
return { x, y }; | ||
} | ||
else if (len === uncompressedLen && header === 0x04) { | ||
const x = Fp.fromBytes(bytes.subarray(1, Fp.BYTES + 1)); | ||
const y = Fp.fromBytes(bytes.subarray(Fp.BYTES + 1, 2 * Fp.BYTES + 1)); | ||
return { x, y }; | ||
} | ||
else { | ||
throw new Error(`Point.fromHex: received invalid point. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`); | ||
} | ||
}, | ||
}); | ||
// Do we need these functions at all? | ||
function numToField(num) { | ||
if (typeof num !== 'bigint') | ||
throw new Error('Expected bigint'); | ||
if (!(_0n <= num && num < Fp.MASK)) | ||
throw new Error(`Expected number < 2^${Fp.BYTES * 8}`); | ||
return Fp.toBytes(num); | ||
} | ||
const numToFieldStr = (num) => (0, utils_js_1.bytesToHex)(numToField(num)); | ||
/** | ||
* Normalizes hex, bytes, Point to Point. Checks for curve equation. | ||
*/ | ||
function normalizePublicKey(publicKey) { | ||
if (publicKey instanceof Point) { | ||
publicKey.assertValidity(); | ||
return publicKey; | ||
} | ||
else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') { | ||
return Point.fromHex(publicKey); | ||
// This can happen because PointType can be instance of different class | ||
} | ||
else | ||
throw new Error(`Unknown type of public key: ${publicKey}`); | ||
} | ||
function isBiggerThanHalfOrder(number) { | ||
const HALF = CURVE_ORDER >> _1n; | ||
return number > HALF; | ||
} | ||
function normalizeS(s) { | ||
return isBiggerThanHalfOrder(s) ? mod.mod(-s, CURVE_ORDER) : s; | ||
} | ||
// Ensures ECDSA message hashes are 32 bytes and < curve order | ||
function _truncateHash(hash, truncateOnly = false) { | ||
const { n, nBitLength } = CURVE; | ||
const byteLength = hash.length; | ||
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits) | ||
let h = (0, utils_js_1.bytesToNumberBE)(hash); | ||
if (delta > 0) | ||
h = h >> BigInt(delta); | ||
if (!truncateOnly && h >= n) | ||
h -= n; | ||
return h; | ||
} | ||
const truncateHash = CURVE.truncateHash || _truncateHash; | ||
/** | ||
* ECDSA signature with its (r, s) properties. Supports DER & compact representations. | ||
@@ -680,6 +763,6 @@ */ | ||
throw new TypeError(`${name}: Expected string or Uint8Array`); | ||
const str = arr ? bytesToHex(hex) : hex; | ||
const str = arr ? (0, utils_js_1.bytesToHex)(hex) : hex; | ||
if (str.length !== 128) | ||
throw new Error(`${name}: Expected 64-byte hex`); | ||
return new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128))); | ||
return new Signature((0, utils_js_1.hexToNumber)(str.slice(0, 64)), (0, utils_js_1.hexToNumber)(str.slice(64, 128))); | ||
} | ||
@@ -692,3 +775,3 @@ // DER encoded ECDSA signature | ||
throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`); | ||
const { r, s } = parseDERSignature(arr ? hex : hexToBytes(hex)); | ||
const { r, s } = parseDERSignature(arr ? hex : (0, utils_js_1.hexToBytes)(hex)); | ||
return new Signature(r, s); | ||
@@ -726,3 +809,3 @@ } | ||
throw new Error('Cannot recover: invalid recovery bit'); | ||
const h = truncateHash(ensureBytes(msgHash)); | ||
const h = truncateHash((0, utils_js_1.ensureBytes)(msgHash)); | ||
const { n } = CURVE; | ||
@@ -756,12 +839,12 @@ const rinv = mod.invert(r, n); | ||
toDERRawBytes(isCompressed = false) { | ||
return hexToBytes(this.toDERHex(isCompressed)); | ||
return (0, utils_js_1.hexToBytes)(this.toDERHex(isCompressed)); | ||
} | ||
toDERHex(isCompressed = false) { | ||
const sHex = sliceDER(numberToHexUnpadded(this.s)); | ||
const sHex = sliceDER((0, utils_js_1.numberToHexUnpadded)(this.s)); | ||
if (isCompressed) | ||
return sHex; | ||
const rHex = sliceDER(numberToHexUnpadded(this.r)); | ||
const rLen = numberToHexUnpadded(rHex.length / 2); | ||
const sLen = numberToHexUnpadded(sHex.length / 2); | ||
const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4); | ||
const rHex = sliceDER((0, utils_js_1.numberToHexUnpadded)(this.r)); | ||
const rLen = (0, utils_js_1.numberToHexUnpadded)(rHex.length / 2); | ||
const sLen = (0, utils_js_1.numberToHexUnpadded)(sHex.length / 2); | ||
const length = (0, utils_js_1.numberToHexUnpadded)(rHex.length / 2 + sHex.length / 2 + 4); | ||
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`; | ||
@@ -771,3 +854,3 @@ } | ||
toCompactRawBytes() { | ||
return hexToBytes(this.toCompactHex()); | ||
return (0, utils_js_1.hexToBytes)(this.toCompactHex()); | ||
} | ||
@@ -779,4 +862,4 @@ toCompactHex() { | ||
const utils = { | ||
mod: modP, | ||
invert: (n, m = CURVE.P) => mod.invert(n, m), | ||
mod: (n, modulo = Fp.ORDER) => mod.mod(n, modulo), | ||
invert: Fp.invert, | ||
isValidPrivateKey(privateKey) { | ||
@@ -801,3 +884,3 @@ try { | ||
*/ | ||
hashToPrivateKey: (hash) => numToField(hashToPrivateScalar(hash, CURVE_ORDER)), | ||
hashToPrivateKey: (hash) => numToField((0, utils_js_1.hashToPrivateScalar)(hash, CURVE_ORDER)), | ||
/** | ||
@@ -807,3 +890,3 @@ * Produces cryptographically secure private key from random of size (nBitLength+64) | ||
*/ | ||
randomPrivateKey: () => utils.hashToPrivateKey(CURVE.randomBytes(fieldLen + 8)), | ||
randomPrivateKey: () => utils.hashToPrivateKey(CURVE.randomBytes(Fp.BYTES + 8)), | ||
/** | ||
@@ -868,4 +951,4 @@ * 1. Returns cached point which you can use to pass to `getSharedSecret` or `#multiply` by it. | ||
function bits2int(bytes) { | ||
const slice = bytes.length > fieldLen ? bytes.slice(0, fieldLen) : bytes; | ||
return bytesToNumberBE(slice); | ||
const slice = bytes.length > Fp.BYTES ? bytes.slice(0, Fp.BYTES) : bytes; | ||
return (0, utils_js_1.bytesToNumberBE)(slice); | ||
} | ||
@@ -886,3 +969,3 @@ function bits2octets(bytes) { | ||
// Step A is ignored, since we already provide hash instead of msg | ||
const h1 = numToField(truncateHash(ensureBytes(msgHash))); | ||
const h1 = numToField(truncateHash((0, utils_js_1.ensureBytes)(msgHash))); | ||
const d = normalizePrivateKey(privateKey); | ||
@@ -894,6 +977,6 @@ // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k') | ||
if (extraEntropy === true) | ||
extraEntropy = CURVE.randomBytes(fieldLen); | ||
const e = ensureBytes(extraEntropy); | ||
if (e.length !== fieldLen) | ||
throw new Error(`sign: Expected ${fieldLen} bytes of extra data`); | ||
extraEntropy = CURVE.randomBytes(Fp.BYTES); | ||
const e = (0, utils_js_1.ensureBytes)(extraEntropy); | ||
if (e.length !== Fp.BYTES) | ||
throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`); | ||
seedArgs.push(e); | ||
@@ -904,3 +987,3 @@ } | ||
// V, 0x00 are done in HmacDRBG constructor. | ||
const seed = concatBytes(...seedArgs); | ||
const seed = (0, utils_js_1.concatBytes)(...seedArgs); | ||
const m = bits2int(h1); | ||
@@ -988,3 +1071,3 @@ return { seed, m, d }; | ||
} | ||
msgHash = ensureBytes(msgHash); | ||
msgHash = (0, utils_js_1.ensureBytes)(msgHash); | ||
} | ||
@@ -1030,1 +1113,2 @@ catch (error) { | ||
} | ||
exports.weierstrass = weierstrass; |
{ | ||
"name": "@noble/curves", | ||
"version": "0.3.2", | ||
"version": "0.4.0", | ||
"description": "Minimal, zero-dependency JS implementation of elliptic curve cryptography", | ||
@@ -11,8 +11,8 @@ "files": [ | ||
"scripts": { | ||
"bench": "node benchmark/index.js", | ||
"bench": "node curve-definitions/benchmark/index.js", | ||
"build": "tsc && tsc -p tsconfig.esm.json", | ||
"build:release": "rollup -c rollup.config.js", | ||
"lint": "prettier --check 'src/**/*.{js,ts}'", | ||
"format": "prettier --write 'src/**/*.{js,ts}'", | ||
"test": "node test/index.test.js" | ||
"lint": "prettier --check 'src/**/*.{js,ts}' 'curve-definitions/src/**/*.{js,ts}'", | ||
"format": "prettier --write 'src/**/*.{js,ts}' 'curve-definitions/src/**/*.{js,ts}'", | ||
"test": "cd curve-definitions; node test/index.test.js" | ||
}, | ||
@@ -26,24 +26,53 @@ "author": "Paul Miller (https://paulmillr.com)", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@noble/hashes": "1.1.5" | ||
}, | ||
"devDependencies": { | ||
"@rollup/plugin-node-resolve": "13.3.0", | ||
"@scure/base": "^1.1.1", | ||
"@scure/bip32": "^1.1.1", | ||
"@scure/bip39": "^1.1.0", | ||
"fast-check": "^3.4.0", | ||
"micro-bmark": "0.2.0", | ||
"micro-should": "^0.2.0", | ||
"prettier": "^2.6.2", | ||
"micro-should": "0.2.0", | ||
"prettier": "2.6.2", | ||
"rollup": "2.75.5", | ||
"typescript": "4.7.3" | ||
}, | ||
"main": "lib/index.js", | ||
"module": "lib/index.js", | ||
"browser": { | ||
"crypto": false, | ||
"./crypto": "./lib/cryptoBrowser.js" | ||
"main": "index.js", | ||
"exports": { | ||
"./edwards": { | ||
"types": "./lib/edwards.d.ts", | ||
"import": "./lib/esm/edwards.js", | ||
"default": "./lib/edwards.js" | ||
}, | ||
"./modular": { | ||
"types": "./lib/modular.d.ts", | ||
"import": "./lib/esm/modular.js", | ||
"default": "./lib/modular.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" | ||
}, | ||
"./bls": { | ||
"types": "./lib/bls.d.ts", | ||
"import": "./lib/esm/bls.js", | ||
"default": "./lib/bls.js" | ||
}, | ||
"./hashToCurve": { | ||
"types": "./lib/hashToCurve.d.ts", | ||
"import": "./lib/esm/hashToCurve.js", | ||
"default": "./lib/hashToCurve.js" | ||
}, | ||
"./group": { | ||
"types": "./lib/group.d.ts", | ||
"import": "./lib/esm/group.js", | ||
"default": "./lib/group.js" | ||
}, | ||
"./utils": { | ||
"types": "./lib/utils.d.ts", | ||
"import": "./lib/esm/utils.js", | ||
"default": "./lib/utils.js" | ||
} | ||
}, | ||
"type": "module", | ||
"keywords": [ | ||
@@ -54,8 +83,2 @@ "elliptic", | ||
"hyperelliptic", | ||
"weierstrass", | ||
"edwards", | ||
"montgomery", | ||
"secp256k1", | ||
"ed25519", | ||
"ed448", | ||
"p256", | ||
@@ -76,2 +99,2 @@ "p384", | ||
] | ||
} | ||
} |
@@ -48,5 +48,5 @@ # noble-curves | ||
import { weierstrass } from '@noble/curves/weierstrass'; // Short Weierstrass curve | ||
import { concatBytes, randomBytes } from '@noble/curves/utils'; | ||
import { sha256 } from '@noble/hashes/sha256'; | ||
import { hmac } from '@noble/hashes/hmac'; | ||
import { concatBytes, randomBytes } from '@noble/hashes/utils'; | ||
@@ -130,2 +130,3 @@ const secp256k1 = weierstrass({ | ||
hash: sha512, | ||
randomBytes, | ||
adjustScalarBytes(bytes) { // optional | ||
@@ -132,0 +133,0 @@ bytes[0] &= 248; |
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
268825
0
6
6058
387
29
1
No
- Removed@noble/hashes@1.1.5
- Removed@noble/hashes@1.1.5(transitive)