@noble/curves
Advanced tools
Comparing version 0.6.0 to 0.6.1
@@ -21,7 +21,7 @@ import { randomBytes } from '@noble/hashes/utils'; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -38,6 +38,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -44,0 +44,0 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; |
@@ -49,3 +49,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
}; | ||
export declare type AbstractCurve<T> = { | ||
export declare type BasicCurve<T> = { | ||
Fp: Field<T>; | ||
@@ -59,8 +59,7 @@ n: bigint; | ||
Gy: T; | ||
wrapPrivateKey?: boolean; | ||
allowInfinityPoint?: boolean; | ||
}; | ||
export declare function validateAbsOpts<FP, T>(curve: AbstractCurve<FP> & T): Readonly<{ | ||
export declare function validateBasic<FP, T>(curve: BasicCurve<FP> & T): Readonly<{ | ||
readonly nBitLength: number; | ||
readonly nByteLength: number; | ||
} & AbstractCurve<FP> & T>; | ||
} & BasicCurve<FP> & T>; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.validateAbsOpts = exports.wNAF = void 0; | ||
exports.validateBasic = exports.wNAF = void 0; | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Abelian group utilities | ||
const modular_js_1 = require("./modular.js"); | ||
const utils_js_1 = require("./utils.js"); | ||
const _0n = BigInt(0); | ||
@@ -128,23 +129,16 @@ const _1n = BigInt(1); | ||
exports.wNAF = wNAF; | ||
function validateAbsOpts(curve) { | ||
function validateBasic(curve) { | ||
(0, modular_js_1.validateField)(curve.Fp); | ||
for (const i of ['n', 'h']) { | ||
const val = curve[i]; | ||
if (typeof val !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
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']) { | ||
const val = curve[i]; | ||
if (val === undefined) | ||
continue; // Optional | ||
if (!Number.isSafeInteger(val)) | ||
throw new Error(`Invalid param ${i}=${val} (${typeof val})`); | ||
} | ||
(0, utils_js_1.validateObject)(curve, { | ||
n: 'bigint', | ||
h: 'bigint', | ||
Gx: 'field', | ||
Gy: 'field', | ||
}, { | ||
nBitLength: 'isSafeInteger', | ||
nByteLength: 'isSafeInteger', | ||
}); | ||
// Set defaults | ||
return Object.freeze({ ...(0, modular_js_1.nLength)(curve.n, curve.nBitLength), ...curve }); | ||
} | ||
exports.validateAbsOpts = validateAbsOpts; | ||
exports.validateBasic = validateBasic; |
@@ -0,4 +1,5 @@ | ||
import * as ut from './utils.js'; | ||
import { FHash, Hex } from './utils.js'; | ||
import { Group, GroupConstructor, AbstractCurve, AffinePoint } from './curve.js'; | ||
export declare type CurveType = AbstractCurve<bigint> & { | ||
import { Group, GroupConstructor, BasicCurve, AffinePoint } from './curve.js'; | ||
export declare type CurveType = BasicCurve<bigint> & { | ||
a: bigint; | ||
@@ -26,7 +27,6 @@ d: bigint; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly d: bigint; | ||
readonly hash: FHash; | ||
readonly hash: ut.FHash; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
@@ -39,3 +39,3 @@ readonly adjustScalarBytes?: ((bytes: Uint8Array) => Uint8Array) | undefined; | ||
}) | undefined; | ||
readonly preHash?: FHash | undefined; | ||
readonly preHash?: ut.FHash | undefined; | ||
readonly mapToCurve?: ((scalar: bigint[]) => AffinePoint<bigint>) | undefined; | ||
@@ -42,0 +42,0 @@ }>; |
@@ -7,2 +7,3 @@ "use strict"; | ||
const modular_js_1 = require("./modular.js"); | ||
const ut = require("./utils.js"); | ||
const utils_js_1 = require("./utils.js"); | ||
@@ -16,20 +17,14 @@ const curve_js_1 = require("./curve.js"); | ||
function validateOpts(curve) { | ||
const opts = (0, curve_js_1.validateAbsOpts)(curve); | ||
if (typeof opts.hash !== 'function') | ||
throw new Error('Invalid hash function'); | ||
for (const i of ['a', 'd']) { | ||
const val = opts[i]; | ||
if (typeof val !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
for (const fn of ['randomBytes']) { | ||
if (typeof opts[fn] !== 'function') | ||
throw new Error(`Invalid ${fn} function`); | ||
} | ||
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) { | ||
if (opts[fn] === undefined) | ||
continue; // Optional | ||
if (typeof opts[fn] !== 'function') | ||
throw new Error(`Invalid ${fn} function`); | ||
} | ||
const opts = (0, curve_js_1.validateBasic)(curve); | ||
ut.validateObject(curve, { | ||
hash: 'function', | ||
a: 'bigint', | ||
d: 'bigint', | ||
randomBytes: 'function', | ||
}, { | ||
adjustScalarBytes: 'function', | ||
domain: 'function', | ||
uvRatio: 'function', | ||
mapToCurve: 'function', | ||
}); | ||
// Set defaults | ||
@@ -269,3 +264,3 @@ return Object.freeze({ ...opts }); | ||
normed[len - 1] = lastByte & ~0x80; // clear last bit | ||
const y = (0, utils_js_1.bytesToNumberLE)(normed); | ||
const y = ut.bytesToNumberLE(normed); | ||
if (y === _0n) { | ||
@@ -300,3 +295,3 @@ // y=0 is allowed | ||
const { x, y } = this.toAffine(); | ||
const bytes = (0, utils_js_1.numberToBytesLE)(y, Fp.BYTES); // each y has 2 x values (x, -y) | ||
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y) | ||
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y | ||
@@ -306,3 +301,3 @@ return bytes; // and use the last byte to encode sign of x | ||
toHex() { | ||
return (0, utils_js_1.bytesToHex)(this.toRawBytes()); // Same as toRawBytes, but returns string. | ||
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string. | ||
} | ||
@@ -319,3 +314,3 @@ } | ||
function modN_LE(hash) { | ||
return modN((0, utils_js_1.bytesToNumberLE)(hash)); | ||
return modN(ut.bytesToNumberLE(hash)); | ||
} | ||
@@ -346,3 +341,3 @@ function isHex(item, err) { | ||
function hashDomainToScalar(context = new Uint8Array(), ...msgs) { | ||
const msg = (0, utils_js_1.concatBytes)(...msgs); | ||
const msg = ut.concatBytes(...msgs); | ||
return modN_LE(cHash(domain(msg, (0, utils_js_1.ensureBytes)(context), !!preHash))); | ||
@@ -362,3 +357,3 @@ } | ||
assertGE0(s); // 0 <= s < l | ||
const res = (0, utils_js_1.concatBytes)(R, (0, utils_js_1.numberToBytesLE)(s, Fp.BYTES)); | ||
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES)); | ||
return (0, utils_js_1.ensureBytes)(res, nByteLength * 2); // 64-byte signature | ||
@@ -376,3 +371,3 @@ } | ||
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P | ||
const s = (0, utils_js_1.bytesToNumberLE)(sig.slice(len, 2 * len)); // 0 <= s < l | ||
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l | ||
const SB = G.multiplyUnsafe(s); | ||
@@ -379,0 +374,0 @@ const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg); |
@@ -23,3 +23,3 @@ "use strict"; | ||
if (typeof str !== 'string') { | ||
throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`); | ||
throw new Error(`utf8ToBytes expected string, got ${typeof str}`); | ||
} | ||
@@ -26,0 +26,0 @@ return new TextEncoder().encode(str); |
@@ -45,3 +45,3 @@ export declare function mod(a: bigint, b: bigint): bigint; | ||
} | ||
export declare function validateField<T>(field: Field<T>): void; | ||
export declare function validateField<T>(field: Field<T>): object; | ||
export declare function FpPow<T>(f: Field<T>, num: T, power: bigint): T; | ||
@@ -48,0 +48,0 @@ export declare function FpInvertBatch<T>(f: Field<T>, nums: T[]): T[]; |
@@ -42,3 +42,2 @@ "use strict"; | ||
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4) | ||
// TODO: Fp version? | ||
function pow2(x, power, modulo) { | ||
@@ -207,14 +206,13 @@ let res = x; | ||
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]})`); | ||
} | ||
const initial = { | ||
ORDER: 'bigint', | ||
MASK: 'bigint', | ||
BYTES: 'isSafeInteger', | ||
BITS: 'isSafeInteger', | ||
}; | ||
const opts = FIELD_FIELDS.reduce((map, val) => { | ||
map[val] = 'function'; | ||
return map; | ||
}, initial); | ||
return (0, utils_js_1.validateObject)(field, opts); | ||
} | ||
@@ -221,0 +219,0 @@ exports.validateField = validateField; |
@@ -10,24 +10,12 @@ "use strict"; | ||
function validateOpts(curve) { | ||
for (const i of ['a24']) { | ||
if (typeof curve[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
for (const i of ['montgomeryBits', 'nByteLength']) { | ||
if (curve[i] === undefined) | ||
continue; // Optional | ||
if (!Number.isSafeInteger(curve[i])) | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) { | ||
if (curve[fn] === undefined) | ||
continue; // Optional | ||
if (typeof curve[fn] !== 'function') | ||
throw new Error(`Invalid ${fn} function`); | ||
} | ||
for (const i of ['Gu']) { | ||
if (curve[i] === undefined) | ||
continue; // Optional | ||
if (typeof curve[i] !== 'string') | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
(0, utils_js_1.validateObject)(curve, { | ||
a24: 'bigint', | ||
}, { | ||
montgomeryBits: 'isSafeInteger', | ||
nByteLength: 'isSafeInteger', | ||
adjustScalarBytes: 'function', | ||
domain: 'function', | ||
powPminus2: 'function', | ||
Gu: 'string', | ||
}); | ||
// Set defaults | ||
@@ -47,27 +35,3 @@ return Object.freeze({ ...curve }); | ||
const powPminus2 = CURVE.powPminus2 || ((x) => (0, modular_js_1.pow)(x, P - BigInt(2), P)); | ||
/** | ||
* Checks for num to be in range: | ||
* For strict == true: `0 < num < max`. | ||
* For strict == false: `0 <= num < max`. | ||
* Converts non-float safe numbers to bigints. | ||
*/ | ||
function normalizeScalar(num, max, strict = true) { | ||
if (!max) | ||
throw new TypeError('Specify max value'); | ||
if (typeof num === 'number' && Number.isSafeInteger(num)) | ||
num = BigInt(num); | ||
if (typeof num === 'bigint' && num < max) { | ||
if (strict) { | ||
if (_0n < num) | ||
return num; | ||
} | ||
else { | ||
if (_0n <= num) | ||
return num; | ||
} | ||
} | ||
throw new TypeError('Expected valid scalar: 0 < scalar < max'); | ||
} | ||
// cswap from RFC7748 | ||
// NOTE: cswap is not from RFC7748! | ||
// cswap from RFC7748. But it is not from RFC7748! | ||
/* | ||
@@ -88,2 +52,7 @@ cswap(swap, x_2, x_3): | ||
} | ||
function assertFieldElement(n) { | ||
if (typeof n === 'bigint' && _0n <= n && n < P) | ||
return n; | ||
throw new Error('Expected valid scalar 0 < scalar < CURVE.P'); | ||
} | ||
// x25519 from 4 | ||
@@ -97,7 +66,6 @@ /** | ||
function montgomeryLadder(pointU, scalar) { | ||
const { P } = CURVE; | ||
const u = normalizeScalar(pointU, P); | ||
const u = assertFieldElement(pointU); | ||
// Section 5: Implementations MUST accept non-canonical values and process them as | ||
// if they had been reduced modulo the field prime. | ||
const k = normalizeScalar(scalar, P); | ||
const k = assertFieldElement(scalar); | ||
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519 | ||
@@ -155,3 +123,2 @@ const a24 = CURVE.a24; | ||
function decodeUCoordinate(uEnc) { | ||
const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes); | ||
// Section 5: When receiving such an array, implementations of X25519 | ||
@@ -161,2 +128,3 @@ // MUST mask the most significant bit in the final byte. | ||
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519 | ||
const u = (0, utils_js_1.ensureBytes)(uEnc, montgomeryBytes); | ||
u[fieldLen - 1] &= 127; // 0b0111_1111 | ||
@@ -171,9 +139,2 @@ return (0, utils_js_1.bytesToNumberLE)(u); | ||
} | ||
/** | ||
* Computes shared secret between private key "scalar" and public key's "u" (x) coordinate. | ||
* We can get 'y' coordinate from 'u', | ||
* but Point.fromHex also wants 'x' coordinate oddity flag, | ||
* and we cannot get 'x' without knowing 'v'. | ||
* Need to add generic conversion between twisted edwards and complimentary curve for JubJub. | ||
*/ | ||
function scalarMult(scalar, u) { | ||
@@ -189,8 +150,3 @@ const pointU = decodeUCoordinate(u); | ||
} | ||
/** | ||
* Computes public key from private. | ||
* Executes scalar multiplication of curve's base point by scalar. | ||
* @param scalar private key | ||
* @returns new public key | ||
*/ | ||
// Computes public key from private. By doing scalar multiplication of base point. | ||
function scalarMultBase(scalar) { | ||
@@ -197,0 +153,0 @@ return scalarMult(scalar, CURVE.Gu); |
@@ -22,3 +22,3 @@ export declare type Hex = Uint8Array | string; | ||
export declare function ensureBytes(hex: Hex, expectedLength?: number): Uint8Array; | ||
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array; | ||
export declare function concatBytes(...arrs: Uint8Array[]): Uint8Array; | ||
export declare function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean; | ||
@@ -29,1 +29,4 @@ export declare function bitLen(n: bigint): number; | ||
export declare const bitMask: (n: number) => bigint; | ||
declare type ValMap = Record<string, string>; | ||
export declare function validateObject(object: object, validators: ValMap, optValidators?: ValMap): object; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.bitMask = exports.bitSet = exports.bitGet = exports.bitLen = exports.equalBytes = exports.concatBytes = exports.ensureBytes = exports.numberToVarBytesBE = exports.numberToBytesLE = exports.numberToBytesBE = exports.bytesToNumberLE = exports.bytesToNumberBE = exports.hexToBytes = exports.hexToNumber = exports.numberToHexUnpadded = exports.bytesToHex = void 0; | ||
exports.validateObject = exports.bitMask = exports.bitSet = exports.bitGet = exports.bitLen = exports.equalBytes = exports.concatBytes = exports.ensureBytes = exports.numberToVarBytesBE = exports.numberToBytesLE = exports.numberToBytesBE = exports.bytesToNumberLE = exports.bytesToNumberBE = exports.hexToBytes = exports.hexToNumber = exports.numberToHexUnpadded = exports.bytesToHex = void 0; | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
@@ -12,3 +12,3 @@ const _0n = BigInt(0); | ||
if (!u8a(bytes)) | ||
throw new Error('Expected Uint8Array'); | ||
throw new Error('Uint8Array expected'); | ||
// pre-caching improves the speed 6x | ||
@@ -29,3 +29,3 @@ let hex = ''; | ||
if (typeof hex !== 'string') | ||
throw new Error('hexToNumber: expected string, got ' + typeof hex); | ||
throw new Error('string expected, got ' + typeof hex); | ||
// Big Endian | ||
@@ -38,5 +38,5 @@ return BigInt(`0x${hex}`); | ||
if (typeof hex !== 'string') | ||
throw new Error('hexToBytes: expected string, got ' + typeof hex); | ||
throw new Error('string expected, got ' + typeof hex); | ||
if (hex.length % 2) | ||
throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length); | ||
throw new Error('hex string is invalid: unpadded ' + hex.length); | ||
const array = new Uint8Array(hex.length / 2); | ||
@@ -48,3 +48,3 @@ for (let i = 0; i < array.length; i++) { | ||
if (Number.isNaN(byte) || byte < 0) | ||
throw new Error('Invalid byte sequence'); | ||
throw new Error('invalid byte sequence'); | ||
array[i] = byte; | ||
@@ -62,3 +62,3 @@ } | ||
if (!u8a(bytes)) | ||
throw new Error('Expected Uint8Array'); | ||
throw new Error('Uint8Array expected'); | ||
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse())); | ||
@@ -72,8 +72,3 @@ } | ||
// Returns variable number bytes (minimal bigint encoding?) | ||
const numberToVarBytesBE = (n) => { | ||
let hex = n.toString(16); | ||
if (hex.length & 1) | ||
hex = '0' + hex; | ||
return hexToBytes(hex); | ||
}; | ||
const numberToVarBytesBE = (n) => hexToBytes(numberToHexUnpadded(n)); | ||
exports.numberToVarBytesBE = numberToVarBytesBE; | ||
@@ -90,15 +85,12 @@ function ensureBytes(hex, expectedLength) { | ||
// Copies several Uint8Arrays into one. | ||
function concatBytes(...arrays) { | ||
if (!arrays.every((b) => u8a(b))) | ||
throw new Error('Uint8Array list expected'); | ||
if (arrays.length === 1) | ||
return arrays[0]; | ||
const length = arrays.reduce((a, arr) => a + arr.length, 0); | ||
const result = new Uint8Array(length); | ||
for (let i = 0, pad = 0; i < arrays.length; i++) { | ||
const arr = arrays[i]; | ||
result.set(arr, pad); | ||
pad += arr.length; | ||
} | ||
return result; | ||
function concatBytes(...arrs) { | ||
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0)); | ||
let pad = 0; // walk through each item, ensure they have proper type | ||
arrs.forEach((a) => { | ||
if (!u8a(a)) | ||
throw new Error('Uint8Array expected'); | ||
r.set(a, pad); | ||
pad += a.length; | ||
}); | ||
return r; | ||
} | ||
@@ -136,1 +128,31 @@ exports.concatBytes = concatBytes; | ||
exports.bitMask = bitMask; | ||
function validateObject(object, validators, optValidators = {}) { | ||
const validatorFns = { | ||
bigint: (val) => typeof val === 'bigint', | ||
function: (val) => typeof val === 'function', | ||
boolean: (val) => typeof val === 'boolean', | ||
string: (val) => typeof val === 'string', | ||
isSafeInteger: (val) => Number.isSafeInteger(val), | ||
array: (val) => Array.isArray(val), | ||
field: (val) => object.Fp.isValid(val), | ||
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen), | ||
}; | ||
// type Key = keyof typeof validators; | ||
const checkField = (fieldName, type, isOptional) => { | ||
const checkVal = validatorFns[type]; | ||
if (typeof checkVal !== 'function') | ||
throw new Error(`Invalid validator "${type}", expected function`); | ||
const val = object[fieldName]; | ||
if (isOptional && val === undefined) | ||
return; | ||
if (!checkVal(val)) { | ||
throw new Error(`Invalid param ${fieldName}=${val} (${typeof val}), expected ${type}`); | ||
} | ||
}; | ||
for (let [fieldName, type] of Object.entries(validators)) | ||
checkField(fieldName, type, false); | ||
for (let [fieldName, type] of Object.entries(optValidators)) | ||
checkField(fieldName, type, true); | ||
return object; | ||
} | ||
exports.validateObject = validateObject; |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import * as mod from './modular.js'; | ||
import * as ut from './utils.js'; | ||
import { Hex, PrivKey, CHash } from './utils.js'; | ||
import { Group, GroupConstructor, AbstractCurve, AffinePoint } from './curve.js'; | ||
import { CHash, Hex, PrivKey } from './utils.js'; | ||
import { Group, GroupConstructor, BasicCurve, AffinePoint } from './curve.js'; | ||
export type { AffinePoint }; | ||
@@ -17,6 +17,6 @@ declare type HmacFnSync = (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
}; | ||
export declare type BasicCurve<T> = AbstractCurve<T> & { | ||
export declare type BasicWCurve<T> = BasicCurve<T> & { | ||
a: T; | ||
b: T; | ||
normalizePrivateKey?: (key: PrivKey) => PrivKey; | ||
allowedPrivateKeyLengths?: readonly number[]; | ||
wrapPrivateKey?: boolean; | ||
@@ -81,3 +81,3 @@ endo?: EndomorphismOpts; | ||
} | ||
export declare type CurvePointsType<T> = BasicCurve<T> & { | ||
export declare type CurvePointsType<T> = BasicWCurve<T> & { | ||
fromBytes: (bytes: Uint8Array) => AffinePoint<T>; | ||
@@ -122,7 +122,7 @@ toBytes: (c: ProjConstructor<T>, point: ProjPointType<T>, compressed: boolean) => Uint8Array; | ||
export declare type PubKey = Hex | ProjPointType<bigint>; | ||
export declare type CurveType = BasicCurve<bigint> & { | ||
lowS?: boolean; | ||
export declare type CurveType = BasicWCurve<bigint> & { | ||
hash: CHash; | ||
hmac: HmacFnSync; | ||
randomBytes: (bytesLength?: number) => Uint8Array; | ||
lowS?: boolean; | ||
bits2int?: (bytes: Uint8Array) => bigint; | ||
@@ -140,14 +140,14 @@ bits2int_modN?: (bytes: Uint8Array) => bigint; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: ut.PrivKey) => ut.PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: EndomorphismOpts | undefined; | ||
readonly isTorsionFree?: ((c: ProjConstructor<bigint>, point: ProjPointType<bigint>) => boolean) | undefined; | ||
readonly clearCofactor?: ((c: ProjConstructor<bigint>, point: ProjPointType<bigint>) => ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: ut.CHash; | ||
readonly hmac: HmacFnSync; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -154,0 +154,0 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; |
@@ -11,17 +11,17 @@ "use strict"; | ||
function validatePointOpts(curve) { | ||
const opts = (0, curve_js_1.validateAbsOpts)(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]})`); | ||
} | ||
for (const i of ['isTorsionFree', 'clearCofactor']) { | ||
if (curve[i] === undefined) | ||
continue; // Optional | ||
if (typeof curve[i] !== 'function') | ||
throw new Error(`Invalid ${i} function`); | ||
} | ||
const endo = opts.endo; | ||
const opts = (0, curve_js_1.validateBasic)(curve); | ||
ut.validateObject(opts, { | ||
a: 'field', | ||
b: 'field', | ||
fromBytes: 'function', | ||
toBytes: 'function', | ||
}, { | ||
allowedPrivateKeyLengths: 'array', | ||
wrapPrivateKey: 'boolean', | ||
isTorsionFree: 'function', | ||
clearCofactor: 'function', | ||
}); | ||
const { endo, Fp, a } = opts; | ||
if (endo) { | ||
if (!Fp.eql(opts.a, Fp.ZERO)) { | ||
if (!Fp.eql(a, Fp.ZERO)) { | ||
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0'); | ||
@@ -35,7 +35,2 @@ } | ||
} | ||
if (typeof opts.fromBytes !== 'function') | ||
throw new Error('Invalid fromBytes function'); | ||
if (typeof opts.toBytes !== 'function') | ||
throw new Error('Invalid fromBytes function'); | ||
// Set defaults | ||
return Object.freeze({ ...opts }); | ||
@@ -121,35 +116,24 @@ } | ||
} | ||
/** | ||
* Validates if a private key is valid and converts it to bigint form. | ||
* Supports two options, that are passed when CURVE is initialized: | ||
* - `normalizePrivateKey()` executed before all checks | ||
* - `wrapPrivateKey` when true, executed after most checks, but before `0 < key < n` | ||
*/ | ||
// Validates if priv key is valid and converts it to bigint. | ||
// Supports options CURVE.normalizePrivateKey and CURVE.wrapPrivateKey. | ||
function normalizePrivateKey(key) { | ||
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n } = CURVE; | ||
if (typeof custom === 'function') | ||
key = custom(key); | ||
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE; | ||
if (lengths && typeof key !== 'bigint') { | ||
if (key instanceof Uint8Array) | ||
key = ut.bytesToHex(key); | ||
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes | ||
if (typeof key !== 'string' || !lengths.includes(key.length)) | ||
throw new Error('Invalid key'); | ||
key = key.padStart(nByteLength * 2, '0'); | ||
} | ||
let num; | ||
if (typeof key === 'bigint') { | ||
// Curve order check is done below | ||
num = key; | ||
try { | ||
num = typeof key === 'bigint' ? key : ut.bytesToNumberBE((0, utils_js_1.ensureBytes)(key, nByteLength)); | ||
} | ||
else if (typeof key === 'string') { | ||
if (key.length !== 2 * groupLen) | ||
throw new Error(`must be ${groupLen} bytes`); | ||
// Validates individual octets | ||
num = ut.bytesToNumberBE((0, utils_js_1.ensureBytes)(key)); | ||
catch (error) { | ||
throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`); | ||
} | ||
else if (key instanceof Uint8Array) { | ||
if (key.length !== groupLen) | ||
throw new Error(`must be ${groupLen} bytes`); | ||
num = ut.bytesToNumberBE(key); | ||
} | ||
else { | ||
throw new Error('private key must be bytes, hex or bigint, not ' + typeof key); | ||
} | ||
// Useful for curves with cofactor != 1 | ||
if (wrapPrivateKey) | ||
num = mod.mod(num, n); | ||
assertGE(num); | ||
num = mod.mod(num, n); // disabled by default, enabled for BLS | ||
assertGE(num); // num in range [1..N-1] | ||
return num; | ||
@@ -509,10 +493,12 @@ } | ||
function validateOpts(curve) { | ||
const opts = (0, curve_js_1.validateAbsOpts)(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 | ||
const opts = (0, curve_js_1.validateBasic)(curve); | ||
ut.validateObject(opts, { | ||
hash: 'hash', | ||
hmac: 'function', | ||
randomBytes: 'function', | ||
}, { | ||
bits2int: 'function', | ||
bits2int_modN: 'function', | ||
lowS: 'boolean', | ||
}); | ||
return Object.freeze({ lowS: true, ...opts }); | ||
@@ -626,3 +612,3 @@ } | ||
else { | ||
throw new Error(`Point.fromHex: received invalid point. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`); | ||
throw new Error(`Point of length ${len} was invalid. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes`); | ||
} | ||
@@ -689,3 +675,3 @@ }, | ||
const u2 = modN(s * ir); // sr^-1 | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1) | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1) | ||
if (!Q) | ||
@@ -813,5 +799,6 @@ throw new Error('point at infinify'); // unsafe is fine: no priv data leaked | ||
if (typeof num !== 'bigint') | ||
throw new Error('Expected bigint'); | ||
throw new Error('bigint expected'); | ||
if (!(_0n <= num && num < ORDER_MASK)) | ||
throw new Error(`Expected number < 2^${CURVE.nBitLength}`); | ||
// n in [0..ORDER_MASK-1] | ||
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`); | ||
// works with order, can have different size than numToField! | ||
@@ -826,2 +813,3 @@ return ut.numberToBytesBE(num, CURVE.nByteLength); | ||
function prepSig(msgHash, privateKey, opts = defaultSigOpts) { | ||
const { hash, randomBytes } = CURVE; | ||
if (msgHash == null) | ||
@@ -834,24 +822,16 @@ throw new Error(`sign: expected valid message hash, not "${msgHash}"`); | ||
if (prehash) | ||
msgHash = CURVE.hash((0, utils_js_1.ensureBytes)(msgHash)); | ||
msgHash = hash((0, utils_js_1.ensureBytes)(msgHash)); | ||
if (lowS == null) | ||
lowS = true; // RFC6979 3.2: we skip step A, because | ||
// Step A is ignored, since we already provide hash instead of msg | ||
// NOTE: instead of bits2int, we calling here truncateHash, since we need | ||
// custom truncation for stark. For other curves it is essentially same as calling bits2int + mod | ||
// However, we cannot later call bits2octets (which is truncateHash + int2octets), since nested bits2int is broken | ||
// for curves where nBitLength % 8 !== 0, so we unwrap it here as int2octets call. | ||
// const bits2octets = (bits)=>int2octets(bytesToNumberBE(truncateHash(bits))) | ||
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash | ||
// We can't later call bits2octets, since nested bits2int is broken for curves | ||
// with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call. | ||
// const bits2octets = (bits) => int2octets(bits2int_modN(bits)) | ||
const h1int = bits2int_modN((0, utils_js_1.ensureBytes)(msgHash)); | ||
const h1octets = int2octets(h1int); | ||
const d = normalizePrivateKey(privateKey); | ||
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k') | ||
const seedArgs = [int2octets(d), h1octets]; | ||
const d = normalizePrivateKey(privateKey); // validate private key, convert to bigint | ||
const seedArgs = [int2octets(d), int2octets(h1int)]; | ||
// extraEntropy. RFC6979 3.6: additional k' (optional). | ||
if (ent != null) { | ||
// RFC6979 3.6: additional k' (optional) | ||
if (ent === true) | ||
ent = CURVE.randomBytes(Fp.BYTES); | ||
const e = (0, utils_js_1.ensureBytes)(ent); | ||
if (e.length !== Fp.BYTES) | ||
throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`); | ||
seedArgs.push(e); | ||
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k') | ||
// Either pass as-is, or generate random bytes. Then validate for being ui8a of size BYTES | ||
seedArgs.push((0, utils_js_1.ensureBytes)(ent === true ? randomBytes(Fp.BYTES) : ent, Fp.BYTES)); | ||
} | ||
@@ -966,3 +946,2 @@ const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2 | ||
verify, | ||
// Point, | ||
ProjectivePoint: Point, | ||
@@ -969,0 +948,0 @@ Signature, |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Abelian group utilities | ||
import { validateField, nLength } from './modular.js'; | ||
import { validateObject } from './utils.js'; | ||
const _0n = BigInt(0); | ||
@@ -124,22 +125,15 @@ const _1n = BigInt(1); | ||
} | ||
export function validateAbsOpts(curve) { | ||
export function validateBasic(curve) { | ||
validateField(curve.Fp); | ||
for (const i of ['n', 'h']) { | ||
const val = curve[i]; | ||
if (typeof val !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
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']) { | ||
const val = curve[i]; | ||
if (val === undefined) | ||
continue; // Optional | ||
if (!Number.isSafeInteger(val)) | ||
throw new Error(`Invalid param ${i}=${val} (${typeof val})`); | ||
} | ||
validateObject(curve, { | ||
n: 'bigint', | ||
h: 'bigint', | ||
Gx: 'field', | ||
Gy: 'field', | ||
}, { | ||
nBitLength: 'isSafeInteger', | ||
nByteLength: 'isSafeInteger', | ||
}); | ||
// Set defaults | ||
return Object.freeze({ ...nLength(curve.n, curve.nBitLength), ...curve }); | ||
} |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y² | ||
import { mod } from './modular.js'; | ||
import { bytesToHex, bytesToNumberLE, concatBytes, ensureBytes, numberToBytesLE, } from './utils.js'; | ||
import { wNAF, validateAbsOpts, } from './curve.js'; | ||
import * as ut from './utils.js'; | ||
import { ensureBytes } from './utils.js'; | ||
import { wNAF, validateBasic } from './curve.js'; | ||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n | ||
@@ -12,20 +13,14 @@ const _0n = BigInt(0); | ||
function validateOpts(curve) { | ||
const opts = validateAbsOpts(curve); | ||
if (typeof opts.hash !== 'function') | ||
throw new Error('Invalid hash function'); | ||
for (const i of ['a', 'd']) { | ||
const val = opts[i]; | ||
if (typeof val !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
for (const fn of ['randomBytes']) { | ||
if (typeof opts[fn] !== 'function') | ||
throw new Error(`Invalid ${fn} function`); | ||
} | ||
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) { | ||
if (opts[fn] === undefined) | ||
continue; // Optional | ||
if (typeof opts[fn] !== 'function') | ||
throw new Error(`Invalid ${fn} function`); | ||
} | ||
const opts = validateBasic(curve); | ||
ut.validateObject(curve, { | ||
hash: 'function', | ||
a: 'bigint', | ||
d: 'bigint', | ||
randomBytes: 'function', | ||
}, { | ||
adjustScalarBytes: 'function', | ||
domain: 'function', | ||
uvRatio: 'function', | ||
mapToCurve: 'function', | ||
}); | ||
// Set defaults | ||
@@ -265,3 +260,3 @@ return Object.freeze({ ...opts }); | ||
normed[len - 1] = lastByte & ~0x80; // clear last bit | ||
const y = bytesToNumberLE(normed); | ||
const y = ut.bytesToNumberLE(normed); | ||
if (y === _0n) { | ||
@@ -296,3 +291,3 @@ // y=0 is allowed | ||
const { x, y } = this.toAffine(); | ||
const bytes = numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y) | ||
const bytes = ut.numberToBytesLE(y, Fp.BYTES); // each y has 2 x values (x, -y) | ||
bytes[bytes.length - 1] |= x & _1n ? 0x80 : 0; // when compressing, it's enough to store y | ||
@@ -302,3 +297,3 @@ return bytes; // and use the last byte to encode sign of x | ||
toHex() { | ||
return bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string. | ||
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string. | ||
} | ||
@@ -315,3 +310,3 @@ } | ||
function modN_LE(hash) { | ||
return modN(bytesToNumberLE(hash)); | ||
return modN(ut.bytesToNumberLE(hash)); | ||
} | ||
@@ -342,3 +337,3 @@ function isHex(item, err) { | ||
function hashDomainToScalar(context = new Uint8Array(), ...msgs) { | ||
const msg = concatBytes(...msgs); | ||
const msg = ut.concatBytes(...msgs); | ||
return modN_LE(cHash(domain(msg, ensureBytes(context), !!preHash))); | ||
@@ -358,3 +353,3 @@ } | ||
assertGE0(s); // 0 <= s < l | ||
const res = concatBytes(R, numberToBytesLE(s, Fp.BYTES)); | ||
const res = ut.concatBytes(R, ut.numberToBytesLE(s, Fp.BYTES)); | ||
return ensureBytes(res, nByteLength * 2); // 64-byte signature | ||
@@ -372,3 +367,3 @@ } | ||
const R = Point.fromHex(sig.slice(0, len), false); // 0 <= R < 2^256: ZIP215 R can be >= P | ||
const s = bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l | ||
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len)); // 0 <= s < l | ||
const SB = G.multiplyUnsafe(s); | ||
@@ -375,0 +370,0 @@ const k = hashDomainToScalar(context, R.toRawBytes(), A.toRawBytes(), msg); |
@@ -19,3 +19,3 @@ import { mod } from './modular.js'; | ||
if (typeof str !== 'string') { | ||
throw new TypeError(`utf8ToBytes expected string, got ${typeof str}`); | ||
throw new Error(`utf8ToBytes expected string, got ${typeof str}`); | ||
} | ||
@@ -22,0 +22,0 @@ return new TextEncoder().encode(str); |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Utilities for modular arithmetics and finite fields | ||
import { bitMask, numberToBytesBE, numberToBytesLE, bytesToNumberBE, bytesToNumberLE, ensureBytes, } from './utils.js'; | ||
import { bitMask, numberToBytesBE, numberToBytesLE, bytesToNumberBE, bytesToNumberLE, ensureBytes, validateObject, } from './utils.js'; | ||
// prettier-ignore | ||
@@ -37,3 +37,2 @@ const _0n = BigInt(0), _1n = BigInt(1), _2n = BigInt(2), _3n = BigInt(3); | ||
// Does x ^ (2 ^ power) mod p. pow2(30, 4) == 30 ^ (2 ^ 4) | ||
// TODO: Fp version? | ||
export function pow2(x, power, modulo) { | ||
@@ -197,14 +196,13 @@ let res = x; | ||
export 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]})`); | ||
} | ||
const initial = { | ||
ORDER: 'bigint', | ||
MASK: 'bigint', | ||
BYTES: 'isSafeInteger', | ||
BITS: 'isSafeInteger', | ||
}; | ||
const opts = FIELD_FIELDS.reduce((map, val) => { | ||
map[val] = 'function'; | ||
return map; | ||
}, initial); | ||
return validateObject(field, opts); | ||
} | ||
@@ -211,0 +209,0 @@ // Generic field functions |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import { mod, pow } from './modular.js'; | ||
import { ensureBytes, numberToBytesLE, bytesToNumberLE } from './utils.js'; | ||
import { bytesToNumberLE, ensureBytes, numberToBytesLE, validateObject } from './utils.js'; | ||
const _0n = BigInt(0); | ||
const _1n = BigInt(1); | ||
function validateOpts(curve) { | ||
for (const i of ['a24']) { | ||
if (typeof curve[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
for (const i of ['montgomeryBits', 'nByteLength']) { | ||
if (curve[i] === undefined) | ||
continue; // Optional | ||
if (!Number.isSafeInteger(curve[i])) | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
for (const fn of ['adjustScalarBytes', 'domain', 'powPminus2']) { | ||
if (curve[fn] === undefined) | ||
continue; // Optional | ||
if (typeof curve[fn] !== 'function') | ||
throw new Error(`Invalid ${fn} function`); | ||
} | ||
for (const i of ['Gu']) { | ||
if (curve[i] === undefined) | ||
continue; // Optional | ||
if (typeof curve[i] !== 'string') | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
} | ||
validateObject(curve, { | ||
a24: 'bigint', | ||
}, { | ||
montgomeryBits: 'isSafeInteger', | ||
nByteLength: 'isSafeInteger', | ||
adjustScalarBytes: 'function', | ||
domain: 'function', | ||
powPminus2: 'function', | ||
Gu: 'string', | ||
}); | ||
// Set defaults | ||
@@ -43,27 +31,3 @@ return Object.freeze({ ...curve }); | ||
const powPminus2 = CURVE.powPminus2 || ((x) => pow(x, P - BigInt(2), P)); | ||
/** | ||
* Checks for num to be in range: | ||
* For strict == true: `0 < num < max`. | ||
* For strict == false: `0 <= num < max`. | ||
* Converts non-float safe numbers to bigints. | ||
*/ | ||
function normalizeScalar(num, max, strict = true) { | ||
if (!max) | ||
throw new TypeError('Specify max value'); | ||
if (typeof num === 'number' && Number.isSafeInteger(num)) | ||
num = BigInt(num); | ||
if (typeof num === 'bigint' && num < max) { | ||
if (strict) { | ||
if (_0n < num) | ||
return num; | ||
} | ||
else { | ||
if (_0n <= num) | ||
return num; | ||
} | ||
} | ||
throw new TypeError('Expected valid scalar: 0 < scalar < max'); | ||
} | ||
// cswap from RFC7748 | ||
// NOTE: cswap is not from RFC7748! | ||
// cswap from RFC7748. But it is not from RFC7748! | ||
/* | ||
@@ -84,2 +48,7 @@ cswap(swap, x_2, x_3): | ||
} | ||
function assertFieldElement(n) { | ||
if (typeof n === 'bigint' && _0n <= n && n < P) | ||
return n; | ||
throw new Error('Expected valid scalar 0 < scalar < CURVE.P'); | ||
} | ||
// x25519 from 4 | ||
@@ -93,7 +62,6 @@ /** | ||
function montgomeryLadder(pointU, scalar) { | ||
const { P } = CURVE; | ||
const u = normalizeScalar(pointU, P); | ||
const u = assertFieldElement(pointU); | ||
// Section 5: Implementations MUST accept non-canonical values and process them as | ||
// if they had been reduced modulo the field prime. | ||
const k = normalizeScalar(scalar, P); | ||
const k = assertFieldElement(scalar); | ||
// The constant a24 is (486662 - 2) / 4 = 121665 for curve25519/X25519 | ||
@@ -151,3 +119,2 @@ const a24 = CURVE.a24; | ||
function decodeUCoordinate(uEnc) { | ||
const u = ensureBytes(uEnc, montgomeryBytes); | ||
// Section 5: When receiving such an array, implementations of X25519 | ||
@@ -157,2 +124,3 @@ // MUST mask the most significant bit in the final byte. | ||
// fieldLen - scalaryBytes = 1 for X448 and = 0 for X25519 | ||
const u = ensureBytes(uEnc, montgomeryBytes); | ||
u[fieldLen - 1] &= 127; // 0b0111_1111 | ||
@@ -167,9 +135,2 @@ return bytesToNumberLE(u); | ||
} | ||
/** | ||
* Computes shared secret between private key "scalar" and public key's "u" (x) coordinate. | ||
* We can get 'y' coordinate from 'u', | ||
* but Point.fromHex also wants 'x' coordinate oddity flag, | ||
* and we cannot get 'x' without knowing 'v'. | ||
* Need to add generic conversion between twisted edwards and complimentary curve for JubJub. | ||
*/ | ||
function scalarMult(scalar, u) { | ||
@@ -185,8 +146,3 @@ const pointU = decodeUCoordinate(u); | ||
} | ||
/** | ||
* Computes public key from private. | ||
* Executes scalar multiplication of curve's base point by scalar. | ||
* @param scalar private key | ||
* @returns new public key | ||
*/ | ||
// Computes public key from private. By doing scalar multiplication of base point. | ||
function scalarMultBase(scalar) { | ||
@@ -193,0 +149,0 @@ return scalarMult(scalar, CURVE.Gu); |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Poseidon Hash: https://eprint.iacr.org/2019/458.pdf, https://www.poseidon-hash.info | ||
import { validateField, FpPow } from './modular.js'; | ||
import { FpPow, validateField } from './modular.js'; | ||
export function validateOpts(opts) { | ||
@@ -5,0 +5,0 @@ const { Fp } = opts; |
@@ -9,3 +9,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
if (!u8a(bytes)) | ||
throw new Error('Expected Uint8Array'); | ||
throw new Error('Uint8Array expected'); | ||
// pre-caching improves the speed 6x | ||
@@ -24,3 +24,3 @@ let hex = ''; | ||
if (typeof hex !== 'string') | ||
throw new Error('hexToNumber: expected string, got ' + typeof hex); | ||
throw new Error('string expected, got ' + typeof hex); | ||
// Big Endian | ||
@@ -32,5 +32,5 @@ return BigInt(`0x${hex}`); | ||
if (typeof hex !== 'string') | ||
throw new Error('hexToBytes: expected string, got ' + typeof hex); | ||
throw new Error('string expected, got ' + typeof hex); | ||
if (hex.length % 2) | ||
throw new Error('hexToBytes: received invalid unpadded hex ' + hex.length); | ||
throw new Error('hex string is invalid: unpadded ' + hex.length); | ||
const array = new Uint8Array(hex.length / 2); | ||
@@ -42,3 +42,3 @@ for (let i = 0; i < array.length; i++) { | ||
if (Number.isNaN(byte) || byte < 0) | ||
throw new Error('Invalid byte sequence'); | ||
throw new Error('invalid byte sequence'); | ||
array[i] = byte; | ||
@@ -54,3 +54,3 @@ } | ||
if (!u8a(bytes)) | ||
throw new Error('Expected Uint8Array'); | ||
throw new Error('Uint8Array expected'); | ||
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse())); | ||
@@ -61,8 +61,3 @@ } | ||
// Returns variable number bytes (minimal bigint encoding?) | ||
export const numberToVarBytesBE = (n) => { | ||
let hex = n.toString(16); | ||
if (hex.length & 1) | ||
hex = '0' + hex; | ||
return hexToBytes(hex); | ||
}; | ||
export const numberToVarBytesBE = (n) => hexToBytes(numberToHexUnpadded(n)); | ||
export function ensureBytes(hex, expectedLength) { | ||
@@ -77,15 +72,12 @@ // Uint8Array.from() instead of hash.slice() because node.js Buffer | ||
// Copies several Uint8Arrays into one. | ||
export function concatBytes(...arrays) { | ||
if (!arrays.every((b) => u8a(b))) | ||
throw new Error('Uint8Array list expected'); | ||
if (arrays.length === 1) | ||
return arrays[0]; | ||
const length = arrays.reduce((a, arr) => a + arr.length, 0); | ||
const result = new Uint8Array(length); | ||
for (let i = 0, pad = 0; i < arrays.length; i++) { | ||
const arr = arrays[i]; | ||
result.set(arr, pad); | ||
pad += arr.length; | ||
} | ||
return result; | ||
export function concatBytes(...arrs) { | ||
const r = new Uint8Array(arrs.reduce((sum, a) => sum + a.length, 0)); | ||
let pad = 0; // walk through each item, ensure they have proper type | ||
arrs.forEach((a) => { | ||
if (!u8a(a)) | ||
throw new Error('Uint8Array expected'); | ||
r.set(a, pad); | ||
pad += a.length; | ||
}); | ||
return r; | ||
} | ||
@@ -117,1 +109,30 @@ export function equalBytes(b1, b2) { | ||
export const bitMask = (n) => (_2n << BigInt(n - 1)) - _1n; | ||
export function validateObject(object, validators, optValidators = {}) { | ||
const validatorFns = { | ||
bigint: (val) => typeof val === 'bigint', | ||
function: (val) => typeof val === 'function', | ||
boolean: (val) => typeof val === 'boolean', | ||
string: (val) => typeof val === 'string', | ||
isSafeInteger: (val) => Number.isSafeInteger(val), | ||
array: (val) => Array.isArray(val), | ||
field: (val) => object.Fp.isValid(val), | ||
hash: (val) => typeof val === 'function' && Number.isSafeInteger(val.outputLen), | ||
}; | ||
// type Key = keyof typeof validators; | ||
const checkField = (fieldName, type, isOptional) => { | ||
const checkVal = validatorFns[type]; | ||
if (typeof checkVal !== 'function') | ||
throw new Error(`Invalid validator "${type}", expected function`); | ||
const val = object[fieldName]; | ||
if (isOptional && val === undefined) | ||
return; | ||
if (!checkVal(val)) { | ||
throw new Error(`Invalid param ${fieldName}=${val} (${typeof val}), expected ${type}`); | ||
} | ||
}; | ||
for (let [fieldName, type] of Object.entries(validators)) | ||
checkField(fieldName, type, false); | ||
for (let [fieldName, type] of Object.entries(optValidators)) | ||
checkField(fieldName, type, true); | ||
return object; | ||
} |
@@ -6,19 +6,19 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import { ensureBytes } from './utils.js'; | ||
import { wNAF, validateAbsOpts, } from './curve.js'; | ||
import { wNAF, validateBasic } from './curve.js'; | ||
function validatePointOpts(curve) { | ||
const opts = validateAbsOpts(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]})`); | ||
} | ||
for (const i of ['isTorsionFree', 'clearCofactor']) { | ||
if (curve[i] === undefined) | ||
continue; // Optional | ||
if (typeof curve[i] !== 'function') | ||
throw new Error(`Invalid ${i} function`); | ||
} | ||
const endo = opts.endo; | ||
const opts = validateBasic(curve); | ||
ut.validateObject(opts, { | ||
a: 'field', | ||
b: 'field', | ||
fromBytes: 'function', | ||
toBytes: 'function', | ||
}, { | ||
allowedPrivateKeyLengths: 'array', | ||
wrapPrivateKey: 'boolean', | ||
isTorsionFree: 'function', | ||
clearCofactor: 'function', | ||
}); | ||
const { endo, Fp, a } = opts; | ||
if (endo) { | ||
if (!Fp.eql(opts.a, Fp.ZERO)) { | ||
if (!Fp.eql(a, Fp.ZERO)) { | ||
throw new Error('Endomorphism can only be defined for Koblitz curves that have a=0'); | ||
@@ -32,7 +32,2 @@ } | ||
} | ||
if (typeof opts.fromBytes !== 'function') | ||
throw new Error('Invalid fromBytes function'); | ||
if (typeof opts.toBytes !== 'function') | ||
throw new Error('Invalid fromBytes function'); | ||
// Set defaults | ||
return Object.freeze({ ...opts }); | ||
@@ -118,35 +113,24 @@ } | ||
} | ||
/** | ||
* Validates if a private key is valid and converts it to bigint form. | ||
* Supports two options, that are passed when CURVE is initialized: | ||
* - `normalizePrivateKey()` executed before all checks | ||
* - `wrapPrivateKey` when true, executed after most checks, but before `0 < key < n` | ||
*/ | ||
// Validates if priv key is valid and converts it to bigint. | ||
// Supports options CURVE.normalizePrivateKey and CURVE.wrapPrivateKey. | ||
function normalizePrivateKey(key) { | ||
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n } = CURVE; | ||
if (typeof custom === 'function') | ||
key = custom(key); | ||
const { allowedPrivateKeyLengths: lengths, nByteLength, wrapPrivateKey, n } = CURVE; | ||
if (lengths && typeof key !== 'bigint') { | ||
if (key instanceof Uint8Array) | ||
key = ut.bytesToHex(key); | ||
// Normalize to hex string, pad. E.g. P521 would norm 130-132 char hex to 132-char bytes | ||
if (typeof key !== 'string' || !lengths.includes(key.length)) | ||
throw new Error('Invalid key'); | ||
key = key.padStart(nByteLength * 2, '0'); | ||
} | ||
let num; | ||
if (typeof key === 'bigint') { | ||
// Curve order check is done below | ||
num = key; | ||
try { | ||
num = typeof key === 'bigint' ? key : ut.bytesToNumberBE(ensureBytes(key, nByteLength)); | ||
} | ||
else if (typeof key === 'string') { | ||
if (key.length !== 2 * groupLen) | ||
throw new Error(`must be ${groupLen} bytes`); | ||
// Validates individual octets | ||
num = ut.bytesToNumberBE(ensureBytes(key)); | ||
catch (error) { | ||
throw new Error(`private key must be ${nByteLength} bytes, hex or bigint, not ${typeof key}`); | ||
} | ||
else if (key instanceof Uint8Array) { | ||
if (key.length !== groupLen) | ||
throw new Error(`must be ${groupLen} bytes`); | ||
num = ut.bytesToNumberBE(key); | ||
} | ||
else { | ||
throw new Error('private key must be bytes, hex or bigint, not ' + typeof key); | ||
} | ||
// Useful for curves with cofactor != 1 | ||
if (wrapPrivateKey) | ||
num = mod.mod(num, n); | ||
assertGE(num); | ||
num = mod.mod(num, n); // disabled by default, enabled for BLS | ||
assertGE(num); // num in range [1..N-1] | ||
return num; | ||
@@ -505,10 +489,12 @@ } | ||
function validateOpts(curve) { | ||
const opts = validateAbsOpts(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 | ||
const opts = validateBasic(curve); | ||
ut.validateObject(opts, { | ||
hash: 'hash', | ||
hmac: 'function', | ||
randomBytes: 'function', | ||
}, { | ||
bits2int: 'function', | ||
bits2int_modN: 'function', | ||
lowS: 'boolean', | ||
}); | ||
return Object.freeze({ lowS: true, ...opts }); | ||
@@ -622,3 +608,3 @@ } | ||
else { | ||
throw new Error(`Point.fromHex: received invalid point. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes, not ${len}`); | ||
throw new Error(`Point of length ${len} was invalid. Expected ${compressedLen} compressed bytes or ${uncompressedLen} uncompressed bytes`); | ||
} | ||
@@ -685,3 +671,3 @@ }, | ||
const u2 = modN(s * ir); // sr^-1 | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1) | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1) | ||
if (!Q) | ||
@@ -809,5 +795,6 @@ throw new Error('point at infinify'); // unsafe is fine: no priv data leaked | ||
if (typeof num !== 'bigint') | ||
throw new Error('Expected bigint'); | ||
throw new Error('bigint expected'); | ||
if (!(_0n <= num && num < ORDER_MASK)) | ||
throw new Error(`Expected number < 2^${CURVE.nBitLength}`); | ||
// n in [0..ORDER_MASK-1] | ||
throw new Error(`bigint expected < 2^${CURVE.nBitLength}`); | ||
// works with order, can have different size than numToField! | ||
@@ -822,2 +809,3 @@ return ut.numberToBytesBE(num, CURVE.nByteLength); | ||
function prepSig(msgHash, privateKey, opts = defaultSigOpts) { | ||
const { hash, randomBytes } = CURVE; | ||
if (msgHash == null) | ||
@@ -830,24 +818,16 @@ throw new Error(`sign: expected valid message hash, not "${msgHash}"`); | ||
if (prehash) | ||
msgHash = CURVE.hash(ensureBytes(msgHash)); | ||
msgHash = hash(ensureBytes(msgHash)); | ||
if (lowS == null) | ||
lowS = true; // RFC6979 3.2: we skip step A, because | ||
// Step A is ignored, since we already provide hash instead of msg | ||
// NOTE: instead of bits2int, we calling here truncateHash, since we need | ||
// custom truncation for stark. For other curves it is essentially same as calling bits2int + mod | ||
// However, we cannot later call bits2octets (which is truncateHash + int2octets), since nested bits2int is broken | ||
// for curves where nBitLength % 8 !== 0, so we unwrap it here as int2octets call. | ||
// const bits2octets = (bits)=>int2octets(bytesToNumberBE(truncateHash(bits))) | ||
lowS = true; // RFC6979 3.2: we skip step A, because we already provide hash | ||
// We can't later call bits2octets, since nested bits2int is broken for curves | ||
// with nBitLength % 8 !== 0. Because of that, we unwrap it here as int2octets call. | ||
// const bits2octets = (bits) => int2octets(bits2int_modN(bits)) | ||
const h1int = bits2int_modN(ensureBytes(msgHash)); | ||
const h1octets = int2octets(h1int); | ||
const d = normalizePrivateKey(privateKey); | ||
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k') | ||
const seedArgs = [int2octets(d), h1octets]; | ||
const d = normalizePrivateKey(privateKey); // validate private key, convert to bigint | ||
const seedArgs = [int2octets(d), int2octets(h1int)]; | ||
// extraEntropy. RFC6979 3.6: additional k' (optional). | ||
if (ent != null) { | ||
// RFC6979 3.6: additional k' (optional) | ||
if (ent === true) | ||
ent = CURVE.randomBytes(Fp.BYTES); | ||
const e = ensureBytes(ent); | ||
if (e.length !== Fp.BYTES) | ||
throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`); | ||
seedArgs.push(e); | ||
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k') | ||
// Either pass as-is, or generate random bytes. Then validate for being ui8a of size BYTES | ||
seedArgs.push(ensureBytes(ent === true ? randomBytes(Fp.BYTES) : ent, Fp.BYTES)); | ||
} | ||
@@ -962,3 +942,2 @@ const seed = ut.concatBytes(...seedArgs); // Step D of RFC6979 3.2 | ||
verify, | ||
// Point, | ||
ProjectivePoint: Point, | ||
@@ -965,0 +944,0 @@ Signature, |
@@ -11,3 +11,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'), | ||
// Field over which we'll do calculations; 2n**224n - 2n**96n + 1n | ||
// Field over which we'll do calculations; | ||
Fp: Fp(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')), | ||
@@ -14,0 +14,0 @@ // Curve order, total count of valid points in the field |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import { createCurve } from './_shortw_utils.js'; | ||
import { sha512 } from '@noble/hashes/sha512'; | ||
import { bytesToHex } from './abstract/utils.js'; | ||
import { Fp as Field } from './abstract/modular.js'; | ||
@@ -36,14 +35,3 @@ import { mapToCurveSimpleSWU } from './abstract/weierstrass.js'; | ||
lowS: false, | ||
// P521 keys could be 130, 131, 132 bytes. We normalize to 132 bytes. | ||
// Does not replace validation; invalid keys would still be rejected. | ||
normalizePrivateKey(key) { | ||
if (typeof key === 'bigint') | ||
return key; | ||
if (key instanceof Uint8Array) | ||
key = bytesToHex(key); | ||
if (typeof key !== 'string' || !([130, 131, 132].includes(key.length))) { | ||
throw new Error('Invalid key'); | ||
} | ||
return key.padStart(66 * 2, '0'); // ensure it's always 132 bytes | ||
}, | ||
allowedPrivateKeyLengths: [130, 131, 132] // P521 keys are variable-length. Normalize to 132b | ||
}, sha512); | ||
@@ -50,0 +38,0 @@ export const secp521r1 = P521; |
@@ -6,3 +6,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import { mapToCurveSimpleSWU } from './abstract/weierstrass.js'; | ||
import { ensureBytes, concatBytes, bytesToNumberBE as bytesToNum, numberToBytesBE, } from './abstract/utils.js'; | ||
import { ensureBytes, concatBytes, bytesToNumberBE as bytesToInt, numberToBytesBE, } from './abstract/utils.js'; | ||
import { randomBytes } from '@noble/hashes/utils'; | ||
@@ -116,16 +116,13 @@ import * as htf from './abstract/hash-to-curve.js'; | ||
} | ||
const toRawX = (point) => point.toRawBytes(true).slice(1); | ||
const pointToBytes = (point) => point.toRawBytes(true).slice(1); | ||
const numTo32b = (n) => numberToBytesBE(n, 32); | ||
const modN = (x) => mod(x, secp256k1N); | ||
const _Point = secp256k1.ProjectivePoint; | ||
const Gmul = (priv) => _Point.fromPrivateKey(priv); | ||
const GmulAdd = (Q, a, b) => _Point.BASE.multiplyAndAddUnsafe(Q, a, b); | ||
function schnorrGetScalar(priv) { | ||
// Let d' = int(sk) | ||
// Fail if d' = 0 or d' ≥ n | ||
// Let P = d'⋅G | ||
// Let d = d' if has_even_y(P), otherwise let d = n - d' . | ||
const point = Gmul(priv); | ||
const scalar = point.hasEvenY() ? priv : modN(-priv); | ||
return { point, scalar, x: toRawX(point) }; | ||
const Point = secp256k1.ProjectivePoint; | ||
const GmulAdd = (Q, a, b) => Point.BASE.multiplyAndAddUnsafe(Q, a, b); | ||
const hex32ToInt = (key) => bytesToInt(ensureBytes(key, 32)); | ||
function schnorrGetExtPubKey(priv) { | ||
let d = typeof priv === 'bigint' ? priv : hex32ToInt(priv); | ||
const point = Point.fromPrivateKey(d); // P = d'⋅G; 0 < d' < n check is done inside | ||
const scalar = point.hasEvenY() ? d : modN(-d); // d = d' if has_even_y(P), otherwise d = n-d' | ||
return { point, scalar, bytes: pointToBytes(point) }; | ||
} | ||
@@ -139,3 +136,3 @@ function lift_x(x) { | ||
y = mod(-y, secp256k1P); // Return the unique point P such that x(P) = x and | ||
const p = new _Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise. | ||
const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise. | ||
p.assertValidity(); | ||
@@ -145,28 +142,22 @@ return p; | ||
function challenge(...args) { | ||
return modN(bytesToNum(taggedHash(TAGS.challenge, ...args))); | ||
return modN(bytesToInt(taggedHash(TAGS.challenge, ...args))); | ||
} | ||
// Schnorr's pubkey is just `x` of Point (BIP340) | ||
function schnorrGetPublicKey(privateKey) { | ||
return toRawX(Gmul(privateKey)); // Let d' = int(sk). Fail if d' = 0 or d' ≥ n. Return bytes(d'⋅G) | ||
return schnorrGetExtPubKey(privateKey).bytes; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G) | ||
} | ||
/** | ||
* Synchronously creates Schnorr signature. Improved security: verifies itself before | ||
* producing an output. | ||
* @param msg message (not message hash) | ||
* @param privateKey private key | ||
* @param auxRand random bytes that would be added to k. Bad RNG won't break it. | ||
*/ | ||
// Creates Schnorr signature as per BIP340. Verifies itself before returning anything. | ||
// auxRand is optional and is not the sole source of k generation: bad CSPRNG won't be dangerous | ||
function schnorrSign(message, privateKey, auxRand = randomBytes(32)) { | ||
if (message == null) | ||
throw new Error(`sign: Expected valid message, not "${message}"`); | ||
const m = ensureBytes(message); | ||
// checks for isWithinCurveOrder | ||
const { x: px, scalar: d } = schnorrGetScalar(bytesToNum(ensureBytes(privateKey, 32))); | ||
const m = ensureBytes(message); // checks for isWithinCurveOrder | ||
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); | ||
const a = ensureBytes(auxRand, 32); // Auxiliary random data a: a 32-byte array | ||
// TODO: replace with proper xor? | ||
const t = numTo32b(d ^ bytesToNum(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a) | ||
const t = numTo32b(d ^ bytesToInt(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a) | ||
const rand = taggedHash(TAGS.nonce, t, px, m); // Let rand = hash/nonce(t || bytes(P) || m) | ||
const k_ = modN(bytesToNum(rand)); // Let k' = int(rand) mod n | ||
const k_ = modN(bytesToInt(rand)); // Let k' = int(rand) mod n | ||
if (k_ === _0n) | ||
throw new Error('sign failed: k is zero'); // Fail if k' = 0. | ||
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k_); // Let R = k'⋅G. | ||
const { point: R, bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G. | ||
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n. | ||
@@ -186,12 +177,12 @@ const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n). | ||
try { | ||
const P = lift_x(bytesToNum(ensureBytes(publicKey, 32))); // P = lift_x(int(pk)); fail if that fails | ||
const P = lift_x(hex32ToInt(publicKey)); // P = lift_x(int(pk)); fail if that fails | ||
const sig = ensureBytes(signature, 64); | ||
const r = bytesToNum(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p. | ||
const r = bytesToInt(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p. | ||
if (!fe(r)) | ||
return false; | ||
const s = bytesToNum(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n. | ||
const s = bytesToInt(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n. | ||
if (!ge(s)) | ||
return false; | ||
const m = ensureBytes(message); | ||
const e = challenge(numTo32b(r), toRawX(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n | ||
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n | ||
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P | ||
@@ -207,7 +198,14 @@ if (!R || !R.hasEvenY() || R.toAffine().x !== r) | ||
export const schnorr = { | ||
// Schnorr's pubkey is just `x` of Point (BIP340) | ||
getPublicKey: schnorrGetPublicKey, | ||
sign: schnorrSign, | ||
verify: schnorrVerify, | ||
utils: { lift_x, int: bytesToNum, taggedHash }, | ||
utils: { | ||
getExtendedPublicKey: schnorrGetExtPubKey, | ||
lift_x, | ||
pointToBytes, | ||
numberToBytesBE, | ||
bytesToNumberBE: bytesToInt, | ||
taggedHash, | ||
mod, | ||
}, | ||
}; | ||
@@ -214,0 +212,0 @@ const isoMap = htf.isogenyMap(Fp, [ |
@@ -98,3 +98,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
} | ||
function getPublicKey0x(privKey, isCompressed) { | ||
function getPublicKey0x(privKey, isCompressed = false) { | ||
return starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed); | ||
@@ -101,0 +101,0 @@ } |
@@ -12,7 +12,7 @@ export declare const P192: Readonly<{ | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -29,6 +29,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -64,7 +64,7 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -81,6 +81,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -87,0 +87,0 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; |
@@ -12,7 +12,7 @@ export declare const P224: Readonly<{ | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -29,6 +29,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -64,7 +64,7 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -81,6 +81,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -87,0 +87,0 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; |
@@ -14,3 +14,3 @@ "use strict"; | ||
b: BigInt('0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4'), | ||
// Field over which we'll do calculations; 2n**224n - 2n**96n + 1n | ||
// Field over which we'll do calculations; | ||
Fp: (0, modular_js_1.Fp)(BigInt('0xffffffffffffffffffffffffffffffff000000000000000000000001')), | ||
@@ -17,0 +17,0 @@ // Curve order, total count of valid points in the field |
@@ -13,7 +13,7 @@ import * as htf from './abstract/hash-to-curve.js'; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -30,6 +30,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -65,7 +65,7 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -82,6 +82,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -88,0 +88,0 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; |
@@ -13,7 +13,7 @@ import * as htf from './abstract/hash-to-curve.js'; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -30,6 +30,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -65,7 +65,7 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: import("./abstract/utils.js").PrivKey) => import("./abstract/utils.js").PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -82,6 +82,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -88,0 +88,0 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; |
@@ -1,2 +0,1 @@ | ||
import { PrivKey } from './abstract/utils.js'; | ||
import * as htf from './abstract/hash-to-curve.js'; | ||
@@ -14,7 +13,7 @@ export declare const P521: Readonly<{ | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: PrivKey) => PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -31,12 +30,12 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; | ||
}>; | ||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
getSharedSecret: (privateA: PrivKey, publicB: import("./abstract/utils.js").Hex, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
getPublicKey: (privateKey: import("./abstract/utils.js").PrivKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/utils.js").Hex, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
verify: (signature: import("./abstract/utils.js").Hex | { | ||
@@ -49,4 +48,4 @@ r: bigint; | ||
utils: { | ||
_normalizePrivateKey: (key: PrivKey) => bigint; | ||
isValidPrivateKey(privateKey: PrivKey): boolean; | ||
_normalizePrivateKey: (key: import("./abstract/utils.js").PrivKey) => bigint; | ||
isValidPrivateKey(privateKey: import("./abstract/utils.js").PrivKey): boolean; | ||
hashToPrivateKey: (hash: import("./abstract/utils.js").Hex) => Uint8Array; | ||
@@ -67,7 +66,7 @@ randomPrivateKey: () => Uint8Array; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: PrivKey) => PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -84,12 +83,12 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: import("./abstract/weierstrass.js").ProjPointType<bigint>) => import("./abstract/weierstrass.js").ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; | ||
}>; | ||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
getSharedSecret: (privateA: PrivKey, publicB: import("./abstract/utils.js").Hex, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
getPublicKey: (privateKey: import("./abstract/utils.js").PrivKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/utils.js").Hex, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
verify: (signature: import("./abstract/utils.js").Hex | { | ||
@@ -102,4 +101,4 @@ r: bigint; | ||
utils: { | ||
_normalizePrivateKey: (key: PrivKey) => bigint; | ||
isValidPrivateKey(privateKey: PrivKey): boolean; | ||
_normalizePrivateKey: (key: import("./abstract/utils.js").PrivKey) => bigint; | ||
isValidPrivateKey(privateKey: import("./abstract/utils.js").PrivKey): boolean; | ||
hashToPrivateKey: (hash: import("./abstract/utils.js").Hex) => Uint8Array; | ||
@@ -106,0 +105,0 @@ randomPrivateKey: () => Uint8Array; |
@@ -7,3 +7,2 @@ "use strict"; | ||
const sha512_1 = require("@noble/hashes/sha512"); | ||
const utils_js_1 = require("./abstract/utils.js"); | ||
const modular_js_1 = require("./abstract/modular.js"); | ||
@@ -40,14 +39,3 @@ const weierstrass_js_1 = require("./abstract/weierstrass.js"); | ||
lowS: false, | ||
// P521 keys could be 130, 131, 132 bytes. We normalize to 132 bytes. | ||
// Does not replace validation; invalid keys would still be rejected. | ||
normalizePrivateKey(key) { | ||
if (typeof key === 'bigint') | ||
return key; | ||
if (key instanceof Uint8Array) | ||
key = (0, utils_js_1.bytesToHex)(key); | ||
if (typeof key !== 'string' || !([130, 131, 132].includes(key.length))) { | ||
throw new Error('Invalid key'); | ||
} | ||
return key.padStart(66 * 2, '0'); // ensure it's always 132 bytes | ||
}, | ||
allowedPrivateKeyLengths: [130, 131, 132] // P521 keys are variable-length. Normalize to 132b | ||
}, sha512_1.sha512); | ||
@@ -54,0 +42,0 @@ exports.secp521r1 = exports.P521; |
@@ -0,3 +1,4 @@ | ||
import { mod } from './abstract/modular.js'; | ||
import { ProjPointType as PointType } from './abstract/weierstrass.js'; | ||
import { Hex, bytesToNumberBE as bytesToNum, PrivKey } from './abstract/utils.js'; | ||
import { Hex, bytesToNumberBE as bytesToInt, PrivKey } from './abstract/utils.js'; | ||
import * as htf from './abstract/hash-to-curve.js'; | ||
@@ -15,7 +16,7 @@ export declare const secp256k1: Readonly<{ | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: PrivKey) => PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -32,6 +33,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: PointType<bigint>) => PointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: import("./abstract/utils.js").CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -57,13 +58,11 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; | ||
declare function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array; | ||
declare function schnorrGetExtPubKey(priv: PrivKey): { | ||
point: PointType<bigint>; | ||
scalar: bigint; | ||
bytes: Uint8Array; | ||
}; | ||
declare function lift_x(x: bigint): PointType<bigint>; | ||
declare function schnorrGetPublicKey(privateKey: PrivKey): Uint8Array; | ||
declare function schnorrGetPublicKey(privateKey: Hex): Uint8Array; | ||
declare function schnorrSign(message: Hex, privateKey: PrivKey, auxRand?: Hex): Uint8Array; | ||
/** | ||
* Synchronously creates Schnorr signature. Improved security: verifies itself before | ||
* producing an output. | ||
* @param msg message (not message hash) | ||
* @param privateKey private key | ||
* @param auxRand random bytes that would be added to k. Bad RNG won't break it. | ||
*/ | ||
declare function schnorrSign(message: Hex, privateKey: Hex, auxRand?: Hex): Uint8Array; | ||
/** | ||
* Verifies Schnorr signature synchronously. | ||
@@ -77,5 +76,9 @@ */ | ||
utils: { | ||
getExtendedPublicKey: typeof schnorrGetExtPubKey; | ||
lift_x: typeof lift_x; | ||
int: typeof bytesToNum; | ||
pointToBytes: (point: PointType<bigint>) => Uint8Array; | ||
numberToBytesBE: (n: bigint, len: number) => Uint8Array; | ||
bytesToNumberBE: typeof bytesToInt; | ||
taggedHash: typeof taggedHash; | ||
mod: typeof mod; | ||
}; | ||
@@ -82,0 +85,0 @@ }; |
@@ -118,16 +118,13 @@ "use strict"; | ||
} | ||
const toRawX = (point) => point.toRawBytes(true).slice(1); | ||
const pointToBytes = (point) => point.toRawBytes(true).slice(1); | ||
const numTo32b = (n) => (0, utils_js_1.numberToBytesBE)(n, 32); | ||
const modN = (x) => (0, modular_js_1.mod)(x, secp256k1N); | ||
const _Point = exports.secp256k1.ProjectivePoint; | ||
const Gmul = (priv) => _Point.fromPrivateKey(priv); | ||
const GmulAdd = (Q, a, b) => _Point.BASE.multiplyAndAddUnsafe(Q, a, b); | ||
function schnorrGetScalar(priv) { | ||
// Let d' = int(sk) | ||
// Fail if d' = 0 or d' ≥ n | ||
// Let P = d'⋅G | ||
// Let d = d' if has_even_y(P), otherwise let d = n - d' . | ||
const point = Gmul(priv); | ||
const scalar = point.hasEvenY() ? priv : modN(-priv); | ||
return { point, scalar, x: toRawX(point) }; | ||
const Point = exports.secp256k1.ProjectivePoint; | ||
const GmulAdd = (Q, a, b) => Point.BASE.multiplyAndAddUnsafe(Q, a, b); | ||
const hex32ToInt = (key) => (0, utils_js_1.bytesToNumberBE)((0, utils_js_1.ensureBytes)(key, 32)); | ||
function schnorrGetExtPubKey(priv) { | ||
let d = typeof priv === 'bigint' ? priv : hex32ToInt(priv); | ||
const point = Point.fromPrivateKey(d); // P = d'⋅G; 0 < d' < n check is done inside | ||
const scalar = point.hasEvenY() ? d : modN(-d); // d = d' if has_even_y(P), otherwise d = n-d' | ||
return { point, scalar, bytes: pointToBytes(point) }; | ||
} | ||
@@ -141,3 +138,3 @@ function lift_x(x) { | ||
y = (0, modular_js_1.mod)(-y, secp256k1P); // Return the unique point P such that x(P) = x and | ||
const p = new _Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise. | ||
const p = new Point(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise. | ||
p.assertValidity(); | ||
@@ -149,20 +146,14 @@ return p; | ||
} | ||
// Schnorr's pubkey is just `x` of Point (BIP340) | ||
function schnorrGetPublicKey(privateKey) { | ||
return toRawX(Gmul(privateKey)); // Let d' = int(sk). Fail if d' = 0 or d' ≥ n. Return bytes(d'⋅G) | ||
return schnorrGetExtPubKey(privateKey).bytes; // d'=int(sk). Fail if d'=0 or d'≥n. Ret bytes(d'⋅G) | ||
} | ||
/** | ||
* Synchronously creates Schnorr signature. Improved security: verifies itself before | ||
* producing an output. | ||
* @param msg message (not message hash) | ||
* @param privateKey private key | ||
* @param auxRand random bytes that would be added to k. Bad RNG won't break it. | ||
*/ | ||
// Creates Schnorr signature as per BIP340. Verifies itself before returning anything. | ||
// auxRand is optional and is not the sole source of k generation: bad CSPRNG won't be dangerous | ||
function schnorrSign(message, privateKey, auxRand = (0, utils_1.randomBytes)(32)) { | ||
if (message == null) | ||
throw new Error(`sign: Expected valid message, not "${message}"`); | ||
const m = (0, utils_js_1.ensureBytes)(message); | ||
// checks for isWithinCurveOrder | ||
const { x: px, scalar: d } = schnorrGetScalar((0, utils_js_1.bytesToNumberBE)((0, utils_js_1.ensureBytes)(privateKey, 32))); | ||
const m = (0, utils_js_1.ensureBytes)(message); // checks for isWithinCurveOrder | ||
const { bytes: px, scalar: d } = schnorrGetExtPubKey(privateKey); | ||
const a = (0, utils_js_1.ensureBytes)(auxRand, 32); // Auxiliary random data a: a 32-byte array | ||
// TODO: replace with proper xor? | ||
const t = numTo32b(d ^ (0, utils_js_1.bytesToNumberBE)(taggedHash(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hash/aux(a) | ||
@@ -173,3 +164,3 @@ const rand = taggedHash(TAGS.nonce, t, px, m); // Let rand = hash/nonce(t || bytes(P) || m) | ||
throw new Error('sign failed: k is zero'); // Fail if k' = 0. | ||
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k_); // Let R = k'⋅G. | ||
const { point: R, bytes: rx, scalar: k } = schnorrGetExtPubKey(k_); // Let R = k'⋅G. | ||
const e = challenge(rx, px, m); // Let e = int(hash/challenge(bytes(R) || bytes(P) || m)) mod n. | ||
@@ -189,3 +180,3 @@ const sig = new Uint8Array(64); // Let sig = bytes(R) || bytes((k + ed) mod n). | ||
try { | ||
const P = lift_x((0, utils_js_1.bytesToNumberBE)((0, utils_js_1.ensureBytes)(publicKey, 32))); // P = lift_x(int(pk)); fail if that fails | ||
const P = lift_x(hex32ToInt(publicKey)); // P = lift_x(int(pk)); fail if that fails | ||
const sig = (0, utils_js_1.ensureBytes)(signature, 64); | ||
@@ -199,3 +190,3 @@ const r = (0, utils_js_1.bytesToNumberBE)(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p. | ||
const m = (0, utils_js_1.ensureBytes)(message); | ||
const e = challenge(numTo32b(r), toRawX(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n | ||
const e = challenge(numTo32b(r), pointToBytes(P), m); // int(challenge(bytes(r)||bytes(P)||m)) mod n | ||
const R = GmulAdd(P, s, modN(-e)); // R = s⋅G - e⋅P | ||
@@ -211,7 +202,14 @@ if (!R || !R.hasEvenY() || R.toAffine().x !== r) | ||
exports.schnorr = { | ||
// Schnorr's pubkey is just `x` of Point (BIP340) | ||
getPublicKey: schnorrGetPublicKey, | ||
sign: schnorrSign, | ||
verify: schnorrVerify, | ||
utils: { lift_x, int: utils_js_1.bytesToNumberBE, taggedHash }, | ||
utils: { | ||
getExtendedPublicKey: schnorrGetExtPubKey, | ||
lift_x, | ||
pointToBytes, | ||
numberToBytesBE: utils_js_1.numberToBytesBE, | ||
bytesToNumberBE: utils_js_1.bytesToNumberBE, | ||
taggedHash, | ||
mod: modular_js_1.mod, | ||
}, | ||
}; | ||
@@ -218,0 +216,0 @@ const isoMap = htf.isogenyMap(Fp, [ |
@@ -19,7 +19,7 @@ import { ProjPointType } from './abstract/weierstrass.js'; | ||
readonly Gy: bigint; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly allowInfinityPoint?: boolean | undefined; | ||
readonly a: bigint; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: cutils.PrivKey) => cutils.PrivKey) | undefined; | ||
readonly allowedPrivateKeyLengths?: readonly number[] | undefined; | ||
readonly wrapPrivateKey?: boolean | undefined; | ||
readonly endo?: { | ||
@@ -36,6 +36,6 @@ beta: bigint; | ||
readonly clearCofactor?: ((c: import("./abstract/weierstrass.js").ProjConstructor<bigint>, point: ProjPointType<bigint>) => ProjPointType<bigint>) | undefined; | ||
lowS: boolean; | ||
readonly hash: cutils.CHash; | ||
readonly hmac: (key: Uint8Array, ...messages: Uint8Array[]) => Uint8Array; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
lowS: boolean; | ||
readonly bits2int?: ((bytes: Uint8Array) => bigint) | undefined; | ||
@@ -42,0 +42,0 @@ readonly bits2int_modN?: ((bytes: Uint8Array) => bigint) | undefined; |
@@ -101,3 +101,3 @@ "use strict"; | ||
} | ||
function getPublicKey0x(privKey, isCompressed) { | ||
function getPublicKey0x(privKey, isCompressed = false) { | ||
return exports.starkCurve.getPublicKey(normalizePrivateKey(privKey), isCompressed); | ||
@@ -104,0 +104,0 @@ } |
{ | ||
"name": "@noble/curves", | ||
"version": "0.6.0", | ||
"version": "0.6.1", | ||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography", | ||
@@ -9,3 +9,3 @@ "files": [ | ||
"scripts": { | ||
"bench": "cd benchmark; node index.js", | ||
"bench": "cd benchmark; node secp256k1.js; node curves.js; node stark.js; node bls.js", | ||
"build": "tsc && tsc -p tsconfig.esm.json", | ||
@@ -34,4 +34,4 @@ "build:release": "rollup -c rollup.config.js", | ||
"fast-check": "3.0.0", | ||
"micro-bmark": "0.2.0", | ||
"micro-should": "0.3.0", | ||
"micro-bmark": "0.3.0", | ||
"micro-should": "0.4.0", | ||
"prettier": "2.8.3", | ||
@@ -38,0 +38,0 @@ "rollup": "2.75.5", |
128
README.md
@@ -10,3 +10,2 @@ # noble-curves | ||
- [Poseidon](https://www.poseidon-hash.info) ZK-friendly hash | ||
- Auditable | ||
- 🏎 [Ultra-fast](#speed), hand-optimized for caveats of JS engines | ||
@@ -16,3 +15,3 @@ - 🔍 Unique tests ensure correctness. Wycheproof vectors included | ||
There are two parts of the package: | ||
Package consists of two parts: | ||
@@ -31,2 +30,3 @@ 1. `abstract/` directory specifies zero-dependency EC algorithms | ||
which had security audits and were developed from 2019 to 2022. | ||
Check out [Upgrading](#upgrading) section if you've used them before. | ||
@@ -451,39 +451,58 @@ ### This library belongs to _noble_ crypto | ||
``` | ||
getPublicKey | ||
secp256k1 x 5,241 ops/sec @ 190μs/op | ||
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 | ||
ed25519 x 8,390 ops/sec @ 119μs/op | ||
ed448 x 3,224 ops/sec @ 310μs/op | ||
sign | ||
secp256k1 x 3,934 ops/sec @ 254μs/op | ||
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 | ||
ed25519 x 4,233 ops/sec @ 236μs/op | ||
ed448 x 1,561 ops/sec @ 640μs/op | ||
verify | ||
secp256k1 x 731 ops/sec @ 1ms/op | ||
P256 x 806 ops/sec @ 1ms/op | ||
P384 x 353 ops/sec @ 2ms/op | ||
P521 x 171 ops/sec @ 5ms/op | ||
ed25519 x 860 ops/sec @ 1ms/op | ||
ed448 x 313 ops/sec @ 3ms/op | ||
getSharedSecret | ||
secp256k1 x 445 ops/sec @ 2ms/op | ||
recoverPublicKey | ||
secp256k1 x 732 ops/sec @ 1ms/op | ||
==== bls12-381 ==== | ||
getPublicKey x 817 ops/sec @ 1ms/op | ||
sign x 50 ops/sec @ 19ms/op | ||
verify x 34 ops/sec @ 28ms/op | ||
pairing x 89 ops/sec @ 11ms/op | ||
==== stark ==== | ||
secp256k1 | ||
init x 57 ops/sec @ 17ms/op | ||
getPublicKey x 4,946 ops/sec @ 202μs/op | ||
sign x 3,914 ops/sec @ 255μs/op | ||
verify x 682 ops/sec @ 1ms/op | ||
getSharedSecret x 427 ops/sec @ 2ms/op | ||
recoverPublicKey x 683 ops/sec @ 1ms/op | ||
schnorr.sign x 539 ops/sec @ 1ms/op | ||
schnorr.verify x 716 ops/sec @ 1ms/op | ||
P256 | ||
init x 30 ops/sec @ 32ms/op | ||
getPublicKey x 5,008 ops/sec @ 199μs/op | ||
sign x 3,970 ops/sec @ 251μs/op | ||
verify x 515 ops/sec @ 1ms/op | ||
P384 | ||
init x 14 ops/sec @ 66ms/op | ||
getPublicKey x 2,434 ops/sec @ 410μs/op | ||
sign x 1,942 ops/sec @ 514μs/op | ||
verify x 206 ops/sec @ 4ms/op | ||
P521 | ||
init x 7 ops/sec @ 126ms/op | ||
getPublicKey x 1,282 ops/sec @ 779μs/op | ||
sign x 1,077 ops/sec @ 928μs/op | ||
verify x 110 ops/sec @ 9ms/op | ||
ed25519 | ||
init x 37 ops/sec @ 26ms/op | ||
getPublicKey x 8,147 ops/sec @ 122μs/op | ||
sign x 3,979 ops/sec @ 251μs/op | ||
verify x 848 ops/sec @ 1ms/op | ||
ed448 | ||
init x 17 ops/sec @ 58ms/op | ||
getPublicKey x 3,083 ops/sec @ 324μs/op | ||
sign x 1,473 ops/sec @ 678μs/op | ||
verify x 323 ops/sec @ 3ms/op | ||
bls12-381 | ||
init x 30 ops/sec @ 33ms/op | ||
getPublicKey x 788 ops/sec @ 1ms/op | ||
sign x 45 ops/sec @ 21ms/op | ||
verify x 32 ops/sec @ 30ms/op | ||
pairing x 88 ops/sec @ 11ms/op | ||
stark | ||
init x 31 ops/sec @ 31ms/op | ||
pedersen | ||
old x 85 ops/sec @ 11ms/op | ||
noble x 1,216 ops/sec @ 822μs/op | ||
├─old x 84 ops/sec @ 11ms/op | ||
└─noble x 802 ops/sec @ 1ms/op | ||
poseidon x 7,466 ops/sec @ 133μs/op | ||
verify | ||
old x 302 ops/sec @ 3ms/op | ||
noble x 698 ops/sec @ 1ms/op | ||
├─old x 300 ops/sec @ 3ms/op | ||
└─noble x 474 ops/sec @ 2ms/op | ||
``` | ||
@@ -493,19 +512,30 @@ | ||
Differences from @noble/secp256k1 1.7: | ||
If you're coming from single-curve noble packages, the following changes need to be kept in mind: | ||
1. Different double() formula (but same addition) | ||
2. Different sqrt() function | ||
3. DRBG supports outputLen bigger than outputLen of hmac | ||
4. Support for different hash functions | ||
- 2d affine (x, y) points have been removed to reduce complexity and improve speed | ||
- Removed `number` support as a type for private keys. `bigint` is still supported | ||
- `mod`, `invert` are no longer present in `utils`. Use `@noble/curves/abstract/modular.js` now. | ||
Differences from @noble/ed25519 1.7: | ||
Upgrading from @noble/secp256k1 1.7: | ||
1. Variable field element lengths between EDDSA/ECDH: | ||
EDDSA (RFC8032) is 456 bits / 57 bytes, ECDH (RFC7748) is 448 bits / 56 bytes | ||
2. Different addition formula (doubling is same) | ||
3. uvRatio differs between curves (half-expected, not only pow fn changes) | ||
4. Point decompression code is different (unexpected), now using generalized formula | ||
5. Domain function was no-op for ed25519, but adds some data even with empty context for ed448 | ||
- Compressed (33-byte) public keys are now returned by default, instead of uncompressed | ||
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required | ||
- `sign()` | ||
- `der`, `recovered` options were removed | ||
- `canonical` was renamed to `lowS` | ||
- Return type is now `{ r: bigint, s: bigint, recovery: number }` instance of `Signature` | ||
- `verify()` | ||
- `strict` was renamed to `lowS` | ||
- `recoverPublicKey()`: moved to sig instance `Signature#recoverPublicKey(msgHash)` | ||
- `Point` was removed: use `ProjectivePoint` in xyz coordinates | ||
- `utils`: Many methods were removed, others were moved to `schnorr` namespace | ||
Upgrading from @noble/ed25519 1.7: | ||
- Methods are now synchronous. Setting `secp.utils.hmacSha256` is no longer required | ||
- ed25519ph, ed25519ctx | ||
- `Point` was removed: use `ExtendedPoint` in xyzt coordinates | ||
- `Signature` was removed | ||
- `getSharedSecret` was removed: use separate x25519 sub-module | ||
## Contributing & testing | ||
@@ -512,0 +542,0 @@ |
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
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
550
581917
12218