@noble/curves
Advanced tools
Comparing version 0.5.1 to 0.5.2
@@ -50,6 +50,4 @@ import { randomBytes } from '@noble/hashes/utils'; | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
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; | ||
signUnhashed: (msg: Uint8Array, 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 | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -62,4 +60,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -66,0 +62,0 @@ _bigintToString: (num: bigint) => string; |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
/** | ||
* BLS (Barreto-Lynn-Scott) family of pairing-friendly curves. | ||
* Implements BLS (Boneh-Lynn-Shacham) signatures. | ||
* Consists of two curves: G1 and G2: | ||
* - G1 is a subgroup of (x, y) E(Fq) over y² = x³ + 4. | ||
* - G2 is a subgroup of ((x₁, x₂+i), (y₁, y₂+i)) E(Fq²) over y² = x³ + 4(1 + i) where i is √-1 | ||
* - Gt, created by bilinear (ate) pairing e(G1, G2), consists of p-th roots of unity in | ||
* Fq^k where k is embedding degree. Only degree 12 is currently supported, 24 is not. | ||
* Pairing is used to aggregate and verify signatures. | ||
* We are using Fp for private keys (shorter) and Fp₂ for signatures (longer). | ||
* Some projects may prefer to swap this relation, it is not supported for now. | ||
*/ | ||
import * as mod from './modular.js'; | ||
import * as utils from './utils.js'; | ||
import * as ut from './utils.js'; | ||
import { Hex, PrivKey } from './utils.js'; | ||
import { htfOpts, stringToBytes, hash_to_field, expand_message_xmd } from './hash-to-curve.js'; | ||
import { htfOpts, stringToBytes, hash_to_field as hashToField, expand_message_xmd as expandMessageXMD } from './hash-to-curve.js'; | ||
import { CurvePointsType, PointType, CurvePointsRes } from './weierstrass.js'; | ||
@@ -37,3 +49,3 @@ declare type Fp = bigint; | ||
htfDefaults: htfOpts; | ||
hash: utils.CHash; | ||
hash: ut.CHash; | ||
randomBytes: (bytesLength?: number) => Uint8Array; | ||
@@ -70,8 +82,5 @@ }; | ||
utils: { | ||
bytesToHex: typeof utils.bytesToHex; | ||
hexToBytes: typeof utils.hexToBytes; | ||
stringToBytes: typeof stringToBytes; | ||
hashToField: typeof hash_to_field; | ||
expandMessageXMD: typeof expand_message_xmd; | ||
mod: typeof mod.mod; | ||
hashToField: typeof hashToField; | ||
expandMessageXMD: typeof expandMessageXMD; | ||
getDSTLabel: () => string; | ||
@@ -78,0 +87,0 @@ setDSTLabel(newLabel: string): void; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.bls = void 0; | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Barreto-Lynn-Scott Curves. A family of pairing friendly curves, with embedding degree = 12 or 24 | ||
// NOTE: only 12 supported for now | ||
// Constructed from pair of weierstrass curves, based pairing logic | ||
const mod = require("./modular.js"); | ||
const utils_js_1 = require("./utils.js"); | ||
// Types | ||
const utils_js_2 = require("./utils.js"); | ||
const ut = require("./utils.js"); | ||
const hash_to_curve_js_1 = require("./hash-to-curve.js"); | ||
@@ -16,8 +9,5 @@ const weierstrass_js_1 = require("./weierstrass.js"); | ||
// Fields looks pretty specific for curve, so for now we need to pass them with options | ||
const Fp = CURVE.Fp; | ||
const Fr = CURVE.Fr; | ||
const Fp2 = CURVE.Fp2; | ||
const Fp6 = CURVE.Fp6; | ||
const Fp12 = CURVE.Fp12; | ||
const BLS_X_LEN = (0, utils_js_1.bitLen)(CURVE.x); | ||
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE; | ||
const BLS_X_LEN = ut.bitLen(CURVE.x); | ||
const groupLen = 32; // TODO: calculate; hardcoded for now | ||
// Pre-compute coefficients for sparse multiplication | ||
@@ -46,3 +36,3 @@ // Point addition and point double calculations is reused for coefficients | ||
Rz = Fp2.mul(t0, t4); // T0 * T4 | ||
if ((0, utils_js_1.bitGet)(CURVE.x, i)) { | ||
if (ut.bitGet(CURVE.x, i)) { | ||
// Addition | ||
@@ -68,2 +58,3 @@ let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz | ||
function millerLoop(ell, g1) { | ||
const { x } = CURVE; | ||
const Px = g1[0]; | ||
@@ -75,3 +66,3 @@ const Py = g1[1]; | ||
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py)); | ||
if ((0, utils_js_1.bitGet)(CURVE.x, i)) { | ||
if (ut.bitGet(x, i)) { | ||
j += 1; | ||
@@ -86,21 +77,5 @@ const F = ell[j]; | ||
} | ||
// bls12-381 is a construction of two curves: | ||
// 1. Fp: (x, y) | ||
// 2. Fp₂: ((x₁, x₂+i), (y₁, y₂+i)) - (complex numbers) | ||
// | ||
// Bilinear Pairing (ate pairing) is used to combine both elements into a paired one: | ||
// Fp₁₂ = e(Fp, Fp2) | ||
// where Fp₁₂ = 12-degree polynomial | ||
// Pairing is used to verify signatures. | ||
// | ||
// We are using Fp for private keys (shorter) and Fp2 for signatures (longer). | ||
// Some projects may prefer to swap this relation, it is not supported for now. | ||
const htfDefaults = { ...CURVE.htfDefaults }; | ||
function isWithinCurveOrder(num) { | ||
return 0 < num && num < CURVE.r; | ||
} | ||
const utils = { | ||
hexToBytes: utils_js_2.hexToBytes, | ||
bytesToHex: utils_js_2.bytesToHex, | ||
mod: mod.mod, | ||
hexToBytes: ut.hexToBytes, | ||
bytesToHex: ut.bytesToHex, | ||
stringToBytes: hash_to_curve_js_1.stringToBytes, | ||
@@ -110,27 +85,6 @@ // TODO: do we need to export it here? | ||
expandMessageXMD: (msg, DST, lenInBytes, H = CURVE.hash) => (0, hash_to_curve_js_1.expand_message_xmd)(msg, DST, lenInBytes, H), | ||
/** | ||
* Can take 40 or more bytes of uniform input e.g. from CSPRNG or KDF | ||
* and convert them into private key, with the modulo bias being negligible. | ||
* As per FIPS 186 B.1.1. | ||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | ||
* @param hash hash output from sha512, or a similar function | ||
* @returns valid private key | ||
*/ | ||
hashToPrivateKey: (hash) => { | ||
hash = (0, utils_js_1.ensureBytes)(hash); | ||
if (hash.length < 40 || hash.length > 1024) | ||
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186'); | ||
// hashToPrivateScalar(hash, CURVE.r) | ||
// NOTE: doesn't add +/-1 | ||
const num = mod.mod((0, utils_js_1.bytesToNumberBE)(hash), CURVE.r); | ||
// This should never happen | ||
if (num === 0n || num === 1n) | ||
throw new Error('Invalid private key'); | ||
return (0, utils_js_1.numberToBytesBE)(num, 32); | ||
}, | ||
randomBytes: (bytesLength = 32) => CURVE.randomBytes(bytesLength), | ||
// NIST SP 800-56A rev 3, section 5.6.1.2.2 | ||
// https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | ||
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(40)), | ||
getDSTLabel: () => htfDefaults.DST, | ||
hashToPrivateKey: (hash) => Fr.toBytes(ut.hashToPrivateScalar(hash, CURVE.r)), | ||
randomBytes: (bytesLength = groupLen) => CURVE.randomBytes(bytesLength), | ||
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)), | ||
getDSTLabel: () => CURVE.htfDefaults.DST, | ||
setDSTLabel(newLabel) { | ||
@@ -141,22 +95,5 @@ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3.1 | ||
} | ||
htfDefaults.DST = newLabel; | ||
CURVE.htfDefaults.DST = newLabel; | ||
}, | ||
}; | ||
function normalizePrivKey(key) { | ||
let int; | ||
if (key instanceof Uint8Array && key.length === 32) | ||
int = (0, utils_js_1.bytesToNumberBE)(key); | ||
else if (typeof key === 'string' && key.length === 64) | ||
int = BigInt(`0x${key}`); | ||
else if (typeof key === 'number' && key > 0 && Number.isSafeInteger(key)) | ||
int = BigInt(key); | ||
else if (typeof key === 'bigint' && key > 0n) | ||
int = key; | ||
else | ||
throw new TypeError('Expected valid private key'); | ||
int = mod.mod(int, CURVE.r); | ||
if (!isWithinCurveOrder(int)) | ||
throw new Error('Private key must be 0 < key < CURVE.r'); | ||
return int; | ||
} | ||
// Point on G1 curve: (x, y) | ||
@@ -215,3 +152,3 @@ const G1 = (0, weierstrass_js_1.weierstrassPoints)({ | ||
msgPoint.assertValidity(); | ||
const sigPoint = msgPoint.multiply(normalizePrivKey(privateKey)); | ||
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey)); | ||
if (message instanceof G2.Point) | ||
@@ -218,0 +155,0 @@ return sigPoint; |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import * as mod from './modular.js'; | ||
import { BasicCurve, Hex, PrivKey } from './utils.js'; | ||
import * as ut from './utils.js'; | ||
import { Hex, PrivKey } from './utils.js'; | ||
import { Group, GroupConstructor } from './group.js'; | ||
import { htfOpts } from './hash-to-curve.js'; | ||
export declare type CHash = { | ||
(message: Uint8Array | string): Uint8Array; | ||
blockLen: number; | ||
outputLen: number; | ||
create(): any; | ||
}; | ||
export declare type CurveType = BasicCurve<bigint> & { | ||
export declare type CurveType = ut.BasicCurve<bigint> & { | ||
a: bigint; | ||
d: bigint; | ||
hash: CHash; | ||
hash: ut.CHash; | ||
randomBytes: (bytesLength?: number) => Uint8Array; | ||
@@ -23,4 +18,3 @@ adjustScalarBytes?: (bytes: Uint8Array) => Uint8Array; | ||
}; | ||
preHash?: CHash; | ||
clearCofactor?: (c: ExtendedPointConstructor, point: ExtendedPointType) => ExtendedPointType; | ||
preHash?: ut.CHash; | ||
htfDefaults?: htfOpts; | ||
@@ -45,3 +39,3 @@ mapToCurve?: (scalar: bigint[]) => { | ||
readonly d: bigint; | ||
readonly hash: CHash; | ||
readonly hash: ut.CHash; | ||
readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
@@ -54,4 +48,3 @@ readonly adjustScalarBytes?: ((bytes: Uint8Array) => Uint8Array) | undefined; | ||
}) | undefined; | ||
readonly preHash?: CHash | undefined; | ||
readonly clearCofactor?: ((c: ExtendedPointConstructor, point: ExtendedPointType) => ExtendedPointType) | undefined; | ||
readonly preHash?: ut.CHash | undefined; | ||
readonly htfDefaults?: htfOpts | undefined; | ||
@@ -119,4 +112,2 @@ readonly mapToCurve?: ((scalar: bigint[]) => { | ||
utils: { | ||
mod: (a: bigint) => bigint; | ||
invert: (number: bigint) => bigint; | ||
randomPrivateKey: () => Uint8Array; | ||
@@ -123,0 +114,0 @@ getExtendedPublicKey: (key: PrivKey) => { |
@@ -7,10 +7,11 @@ "use strict"; | ||
// Differences from @noble/ed25519 1.7: | ||
// 1. Different field element lengths in ed448: | ||
// 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 too (unexpected), now using generalized formula | ||
// 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 | ||
const mod = require("./modular.js"); | ||
const utils_js_1 = require("./utils.js"); // TODO: import * as u from './utils.js'? | ||
const ut = require("./utils.js"); | ||
const utils_js_1 = require("./utils.js"); | ||
const group_js_1 = require("./group.js"); | ||
@@ -23,10 +24,10 @@ const hash_to_curve_js_1 = require("./hash-to-curve.js"); | ||
const _8n = BigInt(8); | ||
// Should be separate from overrides, since overrides can use information about curve (for example nBits) | ||
function validateOpts(curve) { | ||
const opts = (0, utils_js_1.validateOpts)(curve); | ||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen)) | ||
const opts = ut.validateOpts(curve); | ||
if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen)) | ||
throw new Error('Invalid hash function'); | ||
for (const i of ['a', 'd']) { | ||
if (typeof opts[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`); | ||
const val = opts[i]; | ||
if (typeof val !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
@@ -37,9 +38,3 @@ for (const fn of ['randomBytes']) { | ||
} | ||
for (const fn of [ | ||
'adjustScalarBytes', | ||
'domain', | ||
'uvRatio', | ||
'mapToCurve', | ||
'clearCofactor', | ||
]) { | ||
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) { | ||
if (opts[fn] === undefined) | ||
@@ -60,8 +55,3 @@ continue; // Optional | ||
const CURVE_ORDER = CURVE.n; | ||
const fieldLen = Fp.BYTES; // 32 (length of one field element) | ||
if (fieldLen > 2048) | ||
throw new Error('Field lengths over 2048 are not supported'); | ||
const groupLen = CURVE.nByteLength; | ||
// (2n ** 256n).toString(16); | ||
const maxGroupElement = _2n ** BigInt(groupLen * 8); // previous POW_2_256 | ||
const maxGroupElement = _2n ** BigInt(CURVE.nByteLength * 8); | ||
// Function overrides | ||
@@ -71,20 +61,18 @@ const { randomBytes } = CURVE; | ||
// sqrt(u/v) | ||
function _uvRatio(u, v) { | ||
try { | ||
const value = Fp.sqrt(u * Fp.invert(v)); | ||
return { isValid: true, value }; | ||
} | ||
catch (e) { | ||
return { isValid: false, value: _0n }; | ||
} | ||
} | ||
const uvRatio = CURVE.uvRatio || _uvRatio; | ||
const _adjustScalarBytes = (bytes) => bytes; // NOOP | ||
const adjustScalarBytes = CURVE.adjustScalarBytes || _adjustScalarBytes; | ||
function _domain(data, ctx, phflag) { | ||
if (ctx.length || phflag) | ||
throw new Error('Contexts/pre-hash are not supported'); | ||
return data; | ||
} | ||
const domain = CURVE.domain || _domain; // NOOP | ||
const uvRatio = CURVE.uvRatio || | ||
((u, v) => { | ||
try { | ||
return { isValid: true, value: Fp.sqrt(u * Fp.invert(v)) }; | ||
} | ||
catch (e) { | ||
return { isValid: false, value: _0n }; | ||
} | ||
}); | ||
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes); // NOOP | ||
const domain = CURVE.domain || | ||
((data, ctx, phflag) => { | ||
if (ctx.length || phflag) | ||
throw new Error('Contexts/pre-hash are not supported'); | ||
return data; | ||
}); // NOOP | ||
/** | ||
@@ -226,6 +214,4 @@ * Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy). | ||
// an exposed private key e.g. sig verification. | ||
// Allows scalar bigger than curve order, but less than 2^256 | ||
multiplyUnsafe(scalar) { | ||
let n = normalizeScalar(scalar, CURVE_ORDER, false); | ||
const G = ExtendedPoint.BASE; | ||
const P0 = ExtendedPoint.ZERO; | ||
@@ -236,6 +222,9 @@ if (n === _0n) | ||
return this; | ||
if (this.equals(G)) | ||
if (this.equals(ExtendedPoint.BASE)) | ||
return this.wNAF(n); | ||
return wnaf.unsafeLadder(this, n); | ||
} | ||
// Checks if point is of small order. | ||
// If you add something to small order point, you will have "dirty" | ||
// point with torsion component. | ||
// Multiplies point by cofactor and checks if the result is 0. | ||
@@ -245,5 +234,6 @@ isSmallOrder() { | ||
} | ||
// Multiplies point by a very big scalar n and checks if the result is 0. | ||
// Multiplies point by curve order (very big scalar CURVE.n) and checks if the result is 0. | ||
// Returns `false` is the point is dirty. | ||
isTorsionFree() { | ||
return this.multiplyUnsafe(CURVE_ORDER).equals(ExtendedPoint.ZERO); | ||
return wnaf.unsafeLadder(this, CURVE_ORDER).equals(ExtendedPoint.ZERO); | ||
} | ||
@@ -267,9 +257,6 @@ // Converts Extended point to default (x, y) coordinates. | ||
clearCofactor() { | ||
if (CURVE.h === _1n) | ||
return this; // Fast-path | ||
// clear_cofactor(P) := h_eff * P | ||
// hEff = h for ed25519/ed448. Maybe worth moving to params? | ||
if (CURVE.clearCofactor) | ||
return CURVE.clearCofactor(ExtendedPoint, this); | ||
return this.multiplyUnsafe(CURVE.h); | ||
const { h: cofactor } = CURVE; | ||
if (cofactor === _1n) | ||
return this; | ||
return this.multiplyUnsafe(cofactor); | ||
} | ||
@@ -279,3 +266,3 @@ } | ||
ExtendedPoint.ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n); | ||
const wnaf = (0, group_js_1.wNAF)(ExtendedPoint, groupLen * 8); | ||
const wnaf = (0, group_js_1.wNAF)(ExtendedPoint, CURVE.nByteLength * 8); | ||
function assertExtPoint(other) { | ||
@@ -304,3 +291,4 @@ if (!(other instanceof ExtendedPoint)) | ||
const { d, a } = CURVE; | ||
hex = (0, utils_js_1.ensureBytes)(hex, fieldLen); | ||
const len = Fp.BYTES; | ||
hex = (0, utils_js_1.ensureBytes)(hex, len); | ||
// 1. First, interpret the string as an integer in little-endian | ||
@@ -312,9 +300,9 @@ // representation. Bit 255 of this number is the least significant | ||
const normed = hex.slice(); | ||
const lastByte = hex[fieldLen - 1]; | ||
normed[fieldLen - 1] = lastByte & ~0x80; | ||
const y = (0, utils_js_1.bytesToNumberLE)(normed); | ||
const lastByte = hex[len - 1]; | ||
normed[len - 1] = lastByte & ~0x80; | ||
const y = ut.bytesToNumberLE(normed); | ||
if (strict && y >= Fp.ORDER) | ||
throw new Error('Expected 0 < hex < P'); | ||
if (!strict && y >= maxGroupElement) | ||
throw new Error('Expected 0 < hex < 2**256'); | ||
throw new Error('Expected 0 < hex < CURVE.n'); | ||
// 2. To recover the x-coordinate, the curve equation implies | ||
@@ -351,4 +339,4 @@ // Ed25519: x² = (y² - 1) / (d y² + 1) (mod p). | ||
toRawBytes() { | ||
const bytes = (0, utils_js_1.numberToBytesLE)(this.y, fieldLen); | ||
bytes[fieldLen - 1] |= this.x & _1n ? 0x80 : 0; | ||
const bytes = ut.numberToBytesLE(this.y, Fp.BYTES); | ||
bytes[Fp.BYTES - 1] |= this.x & _1n ? 0x80 : 0; | ||
return bytes; | ||
@@ -358,4 +346,6 @@ } | ||
toHex() { | ||
return (0, utils_js_1.bytesToHex)(this.toRawBytes()); | ||
return ut.bytesToHex(this.toRawBytes()); | ||
} | ||
// Determines if point is in prime-order subgroup. | ||
// Returns `false` is the point is dirty. | ||
isTorsionFree() { | ||
@@ -395,8 +385,8 @@ return ExtendedPoint.fromAffine(this).isTorsionFree(); | ||
static hashToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
const { mapToCurve, htfDefaults } = CURVE; | ||
if (!mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = (0, utils_js_1.ensureBytes)(msg); | ||
const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 2, { ...CURVE.htfDefaults, ...options }); | ||
const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]); | ||
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]); | ||
const u = (0, hash_to_curve_js_1.hash_to_field)((0, utils_js_1.ensureBytes)(msg), 2, { ...htfDefaults, ...options }); | ||
const { x: x0, y: y0 } = mapToCurve(u[0]); | ||
const { x: x1, y: y1 } = mapToCurve(u[1]); | ||
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor(); | ||
@@ -407,7 +397,7 @@ return p; | ||
static encodeToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
const { mapToCurve, htfDefaults } = CURVE; | ||
if (!mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = (0, utils_js_1.ensureBytes)(msg); | ||
const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 1, { ...CURVE.htfDefaults, ...options }); | ||
const { x, y } = CURVE.mapToCurve(u[0]); | ||
const u = (0, hash_to_curve_js_1.hash_to_field)((0, utils_js_1.ensureBytes)(msg), 1, { ...htfDefaults, ...options }); | ||
const { x, y } = mapToCurve(u[0]); | ||
return new Point(x, y).clearCofactor(); | ||
@@ -432,5 +422,6 @@ } | ||
static fromHex(hex) { | ||
const bytes = (0, utils_js_1.ensureBytes)(hex, 2 * fieldLen); | ||
const r = Point.fromHex(bytes.slice(0, fieldLen), false); | ||
const s = (0, utils_js_1.bytesToNumberLE)(bytes.slice(fieldLen, 2 * fieldLen)); | ||
const len = Fp.BYTES; | ||
const bytes = (0, utils_js_1.ensureBytes)(hex, 2 * len); | ||
const r = Point.fromHex(bytes.slice(0, len), false); | ||
const s = ut.bytesToNumberLE(bytes.slice(len, 2 * len)); | ||
return new Signature(r, s); | ||
@@ -447,11 +438,11 @@ } | ||
toRawBytes() { | ||
return (0, utils_js_1.concatBytes)(this.r.toRawBytes(), (0, utils_js_1.numberToBytesLE)(this.s, fieldLen)); | ||
return ut.concatBytes(this.r.toRawBytes(), ut.numberToBytesLE(this.s, Fp.BYTES)); | ||
} | ||
toHex() { | ||
return (0, utils_js_1.bytesToHex)(this.toRawBytes()); | ||
return ut.bytesToHex(this.toRawBytes()); | ||
} | ||
} | ||
// Little-endian SHA512 with modulo n | ||
function modlLE(hash) { | ||
return mod.mod((0, utils_js_1.bytesToNumberLE)(hash), CURVE_ORDER); | ||
function modnLE(hash) { | ||
return mod.mod(ut.bytesToNumberLE(hash), CURVE_ORDER); | ||
} | ||
@@ -467,3 +458,3 @@ /** | ||
throw new TypeError('Specify max value'); | ||
if (typeof num === 'number' && Number.isSafeInteger(num)) | ||
if (ut.isPositiveInt(num)) | ||
num = BigInt(num); | ||
@@ -480,32 +471,26 @@ if (typeof num === 'bigint' && num < max) { | ||
} | ||
throw new TypeError('Expected valid scalar: 0 < scalar < max'); | ||
throw new TypeError(`Expected valid scalar: 0 < scalar < ${max}`); | ||
} | ||
function checkPrivateKey(key) { | ||
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */ | ||
function getExtendedPublicKey(key) { | ||
const groupLen = CURVE.nByteLength; | ||
// Normalize bigint / number / string to Uint8Array | ||
key = | ||
typeof key === 'bigint' || typeof key === 'number' | ||
? (0, utils_js_1.numberToBytesLE)(normalizeScalar(key, maxGroupElement), groupLen) | ||
: (0, utils_js_1.ensureBytes)(key); | ||
if (key.length !== groupLen) | ||
throw new Error(`Expected ${groupLen} bytes, got ${key.length}`); | ||
return key; | ||
} | ||
// Takes 64 bytes | ||
function getKeyFromHash(hashed) { | ||
// First 32 bytes of 64b uniformingly random input are taken, | ||
// clears 3 bits of it to produce a random field element. | ||
const keyb = typeof key === 'bigint' || typeof key === 'number' | ||
? ut.numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen) | ||
: key; | ||
// Hash private key with curve's hash function to produce uniformingly random input | ||
// We check byte lengths e.g.: ensureBytes(64, hash(ensureBytes(32, key))) | ||
const hashed = (0, utils_js_1.ensureBytes)(CURVE.hash((0, utils_js_1.ensureBytes)(keyb, groupLen)), 2 * groupLen); | ||
// First half's bits are cleared to produce a random field element. | ||
const head = adjustScalarBytes(hashed.slice(0, groupLen)); | ||
// Second 32 bytes is called key prefix (5.1.6) | ||
// Second half is called key prefix (5.1.6) | ||
const prefix = hashed.slice(groupLen, 2 * groupLen); | ||
// The actual private scalar | ||
const scalar = modlLE(head); | ||
const scalar = modnLE(head); | ||
// Point on Edwards curve aka public key | ||
const point = Point.BASE.multiply(scalar); | ||
// Uint8Array representation | ||
const pointBytes = point.toRawBytes(); | ||
return { head, prefix, scalar, point, pointBytes }; | ||
} | ||
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */ | ||
function getExtendedPublicKey(key) { | ||
return getKeyFromHash(CURVE.hash(checkPrivateKey(key))); | ||
} | ||
/** | ||
@@ -522,3 +507,3 @@ * Calculates ed25519 public key. RFC8032 5.1.5 | ||
context = (0, utils_js_1.ensureBytes)(context); | ||
return modlLE(CURVE.hash(domain(message, context, !!CURVE.preHash))); | ||
return modnLE(CURVE.hash(domain(message, context, !!CURVE.preHash))); | ||
} | ||
@@ -531,5 +516,5 @@ /** Signs message with privateKey. RFC8032 5.1.6 */ | ||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey); | ||
const r = hashDomainToScalar((0, utils_js_1.concatBytes)(prefix, message), context); | ||
const r = hashDomainToScalar(ut.concatBytes(prefix, message), context); | ||
const R = Point.BASE.multiply(r); // R = rG | ||
const k = hashDomainToScalar((0, utils_js_1.concatBytes)(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg) | ||
const k = hashDomainToScalar(ut.concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg) | ||
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp | ||
@@ -573,7 +558,7 @@ return new Signature(R, s).toRawBytes(); | ||
const SB = ExtendedPoint.BASE.multiplyUnsafe(s); | ||
const k = hashDomainToScalar((0, utils_js_1.concatBytes)(r.toRawBytes(), publicKey.toRawBytes(), message), context); | ||
const k = hashDomainToScalar(ut.concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context); | ||
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k); | ||
const RkA = ExtendedPoint.fromAffine(r).add(kA); | ||
// [8][S]B = [8]R + [8][k]A' | ||
return RkA.subtract(SB).multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO); | ||
return RkA.subtract(SB).clearCofactor().equals(ExtendedPoint.ZERO); | ||
} | ||
@@ -584,8 +569,6 @@ // Enable precomputes. Slows down first publicKey computation by 20ms. | ||
getExtendedPublicKey, | ||
mod: modP, | ||
invert: Fp.invert, | ||
/** | ||
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare). | ||
*/ | ||
hashToPrivateScalar: (hash) => (0, utils_js_1.hashToPrivateScalar)(hash, CURVE_ORDER, true), | ||
hashToPrivateScalar: (hash) => ut.hashToPrivateScalar(hash, CURVE_ORDER, true), | ||
/** | ||
@@ -595,3 +578,3 @@ * ed25519 private keys are uniform 32-bit strings. We do not need to check for | ||
*/ | ||
randomPrivateKey: () => randomBytes(fieldLen), | ||
randomPrivateKey: () => randomBytes(Fp.BYTES), | ||
/** | ||
@@ -598,0 +581,0 @@ * We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT |
@@ -64,2 +64,3 @@ "use strict"; | ||
while (a !== _0n) { | ||
// JIT applies optimization if those two lines follow each other | ||
const q = b / a; | ||
@@ -79,3 +80,4 @@ const r = b % a; | ||
// Tonelli-Shanks algorithm | ||
// https://eprint.iacr.org/2012/685.pdf (page 12) | ||
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12) | ||
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks | ||
function tonelliShanks(P) { | ||
@@ -90,3 +92,3 @@ // Legendre constant: used to calculate Legendre symbol (a | p), | ||
// Step 1: By factoring out powers of 2 from p - 1, | ||
// find q and s such that p - 1 = q2s with q odd | ||
// find q and s such that p - 1 = q*(2^s) with q odd | ||
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++) | ||
@@ -110,27 +112,28 @@ ; | ||
return function tonelliSlow(Fp, n) { | ||
// Step 0: Check that n is indeed a square: (n | p) must be ≡ 1 | ||
if (Fp.pow(n, legendreC) !== Fp.ONE) | ||
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1 | ||
if (Fp.pow(n, legendreC) === Fp.negate(Fp.ONE)) | ||
throw new Error('Cannot find square root'); | ||
let s = S; | ||
let c = pow(Z, Q, P); | ||
let r = Fp.pow(n, Q1div2); | ||
let t = Fp.pow(n, Q); | ||
let t2 = Fp.ZERO; | ||
while (!Fp.equals(Fp.sub(t, Fp.ONE), Fp.ZERO)) { | ||
t2 = Fp.square(t); | ||
let i; | ||
for (i = 1; i < s; i++) { | ||
// stop if t2-1 == 0 | ||
if (Fp.equals(Fp.sub(t2, Fp.ONE), Fp.ZERO)) | ||
let r = S; | ||
// TODO: will fail at Fp2/etc | ||
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b | ||
let x = Fp.pow(n, Q1div2); // first guess at the square root | ||
let b = Fp.pow(n, Q); // first guess at the fudge factor | ||
while (!Fp.equals(b, Fp.ONE)) { | ||
if (Fp.equals(b, Fp.ZERO)) | ||
return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0) | ||
// Find m such b^(2^m)==1 | ||
let m = 1; | ||
for (let t2 = Fp.square(b); m < r; m++) { | ||
if (Fp.equals(t2, Fp.ONE)) | ||
break; | ||
// t2 *= t2 | ||
t2 = Fp.square(t2); | ||
t2 = Fp.square(t2); // t2 *= t2 | ||
} | ||
let b = pow(c, BigInt(1 << (s - i - 1)), P); | ||
r = Fp.mul(r, b); | ||
c = mod(b * b, P); | ||
t = Fp.mul(t, c); | ||
s = i; | ||
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow | ||
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1) | ||
g = Fp.square(ge); // g = ge * ge | ||
x = Fp.mul(x, ge); // x *= ge | ||
b = Fp.mul(b, g); // b *= g | ||
r = m; | ||
} | ||
return r; | ||
return x; | ||
}; | ||
@@ -137,0 +140,0 @@ } |
@@ -17,3 +17,3 @@ "use strict"; | ||
continue; // Optional | ||
if (!Number.isSafeInteger(curve[i])) | ||
if (!(0, utils_js_1.isPositiveInt)(curve[i])) | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
@@ -20,0 +20,0 @@ } |
@@ -25,2 +25,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
}; | ||
export declare function isPositiveInt(num: any): num is number; | ||
export declare function validateOpts<FP, T>(curve: BasicCurve<FP> & T): Readonly<{ | ||
@@ -45,10 +46,11 @@ readonly nBitLength: number; | ||
/** | ||
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility. | ||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF | ||
* and convert them into private scalar, with the modulo bias being neglible. | ||
* As per FIPS 186 B.4.1. | ||
* Needs at least 40 bytes of input for 32-byte private key. | ||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | ||
* @param hash hash output from sha512, or a similar function | ||
* @param hash hash output from SHA3 or a similar function | ||
* @returns valid private scalar | ||
*/ | ||
export declare function hashToPrivateScalar(hash: Hex, CURVE_ORDER: bigint, isLE?: boolean): bigint; | ||
export declare function hashToPrivateScalar(hash: Hex, groupOrder: bigint, isLE?: boolean): bigint; | ||
export declare function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean; | ||
@@ -55,0 +57,0 @@ export declare function bitLen(n: bigint): number; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.bitMask = exports.bitSet = exports.bitGet = exports.bitLen = exports.equalBytes = exports.hashToPrivateScalar = exports.nLength = exports.concatBytes = exports.ensureBytes = exports.numberToBytesLE = exports.numberToBytesBE = exports.bytesToNumberLE = exports.bytesToNumberBE = exports.hexToBytes = exports.hexToNumber = exports.numberToHexUnpadded = exports.bytesToHex = exports.validateOpts = void 0; | ||
exports.bitMask = exports.bitSet = exports.bitGet = exports.bitLen = exports.equalBytes = exports.hashToPrivateScalar = exports.nLength = exports.concatBytes = exports.ensureBytes = exports.numberToBytesLE = exports.numberToBytesBE = exports.bytesToNumberLE = exports.bytesToNumberBE = exports.hexToBytes = exports.hexToNumber = exports.numberToHexUnpadded = exports.bytesToHex = exports.validateOpts = exports.isPositiveInt = void 0; | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
@@ -9,7 +9,13 @@ const mod = require("./modular.js"); | ||
const _2n = BigInt(2); | ||
// Bans floats and integers above 2^53-1 | ||
function isPositiveInt(num) { | ||
return typeof num === 'number' && Number.isSafeInteger(num) && num > 0; | ||
} | ||
exports.isPositiveInt = isPositiveInt; | ||
function validateOpts(curve) { | ||
mod.validateField(curve.Fp); | ||
for (const i of ['n', 'h']) { | ||
if (typeof curve[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
const val = curve[i]; | ||
if (typeof val !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
@@ -21,6 +27,7 @@ if (!curve.Fp.isValid(curve.Gx)) | ||
for (const i of ['nBitLength', 'nByteLength']) { | ||
if (curve[i] === undefined) | ||
const val = curve[i]; | ||
if (val === undefined) | ||
continue; // Optional | ||
if (!Number.isSafeInteger(curve[i])) | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
if (!isPositiveInt(val)) | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
@@ -124,17 +131,18 @@ // Set defaults | ||
/** | ||
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility. | ||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF | ||
* and convert them into private scalar, with the modulo bias being neglible. | ||
* As per FIPS 186 B.4.1. | ||
* Needs at least 40 bytes of input for 32-byte private key. | ||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | ||
* @param hash hash output from sha512, or a similar function | ||
* @param hash hash output from SHA3 or a similar function | ||
* @returns valid private scalar | ||
*/ | ||
function hashToPrivateScalar(hash, CURVE_ORDER, isLE = false) { | ||
function hashToPrivateScalar(hash, groupOrder, isLE = false) { | ||
hash = ensureBytes(hash); | ||
const orderLen = nLength(CURVE_ORDER).nByteLength; | ||
const minLen = orderLen + 8; | ||
if (orderLen < 16 || hash.length < minLen || hash.length > 1024) | ||
throw new Error('Expected valid bytes of private key as per FIPS 186'); | ||
const hashLen = hash.length; | ||
const minLen = nLength(groupOrder).nByteLength + 8; | ||
if (minLen < 24 || hashLen < minLen || hashLen > 1024) | ||
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`); | ||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash); | ||
return mod.mod(num, CURVE_ORDER - _1n) + _1n; | ||
return mod.mod(num, groupOrder - _1n) + _1n; | ||
} | ||
@@ -141,0 +149,0 @@ exports.hashToPrivateScalar = hashToPrivateScalar; |
/*! 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 } from './utils.js'; | ||
import * as utils from './utils.js'; | ||
import { htfOpts } from './hash-to-curve.js'; | ||
@@ -17,6 +17,7 @@ import { Group, GroupConstructor } from './group.js'; | ||
}; | ||
export declare type BasicCurve<T> = utils.BasicCurve<T> & { | ||
export declare type BasicCurve<T> = ut.BasicCurve<T> & { | ||
a: T; | ||
b: T; | ||
normalizePrivateKey?: (key: PrivKey) => PrivKey; | ||
wrapPrivateKey?: boolean; | ||
endo?: EndomorphismOpts; | ||
@@ -32,3 +33,3 @@ isTorsionFree?: (c: ProjectiveConstructor<T>, point: ProjectivePointType<T>) => boolean; | ||
declare type Entropy = Hex | true; | ||
declare type SignOpts = { | ||
export declare type SignOpts = { | ||
lowS?: boolean; | ||
@@ -132,3 +133,3 @@ extraEntropy?: Entropy; | ||
lowS?: boolean; | ||
hash: utils.CHash; | ||
hash: ut.CHash; | ||
hmac: HmacFnSync; | ||
@@ -151,3 +152,3 @@ randomBytes: (bytesLength?: number) => Uint8Array; | ||
readonly b: bigint; | ||
readonly normalizePrivateKey?: ((key: PrivKey) => PrivKey) | undefined; | ||
readonly normalizePrivateKey?: ((key: ut.PrivKey) => ut.PrivKey) | undefined; | ||
readonly endo?: EndomorphismOpts | undefined; | ||
@@ -162,3 +163,3 @@ readonly isTorsionFree?: ((c: ProjectiveConstructor<bigint>, point: ProjectivePointType<bigint>) => boolean) | undefined; | ||
lowS: boolean; | ||
readonly hash: utils.CHash; | ||
readonly hash: ut.CHash; | ||
readonly hmac: HmacFnSync; | ||
@@ -173,2 +174,3 @@ readonly randomBytes: (bytesLength?: number | undefined) => Uint8Array; | ||
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType; | ||
signUnhashed: (msg: Uint8Array, privKey: PrivKey, opts?: SignOpts) => SignatureType; | ||
verify: (signature: Hex | SignatureType, msgHash: Hex, publicKey: PubKey, opts?: { | ||
@@ -181,4 +183,2 @@ lowS?: boolean; | ||
utils: { | ||
mod: (a: bigint, b?: bigint) => bigint; | ||
invert: (number: bigint, modulo?: bigint) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -185,0 +185,0 @@ _bigintToString: (num: bigint) => string; |
@@ -6,4 +6,2 @@ "use strict"; | ||
exports.mapToCurveSimpleSWU = exports.SWUFpSqrtRatio = exports.weierstrass = exports.weierstrassPoints = void 0; | ||
// TODO: sync vs async naming | ||
// TODO: default randomBytes | ||
// Differences from @noble/secp256k1 1.7: | ||
@@ -14,8 +12,9 @@ // 1. Different double() formula (but same addition) | ||
// 4. DRBG supports outputLen bigger than outputLen of hmac | ||
// 5. Support for different hash functions | ||
const mod = require("./modular.js"); | ||
const ut = require("./utils.js"); | ||
const utils_js_1 = require("./utils.js"); | ||
const utils = require("./utils.js"); | ||
const hash_to_curve_js_1 = require("./hash-to-curve.js"); | ||
const group_js_1 = require("./group.js"); | ||
// DER encoding utilities | ||
// ASN.1 DER encoding utilities | ||
class DERError extends Error { | ||
@@ -26,42 +25,40 @@ constructor(message) { | ||
} | ||
function sliceDER(s) { | ||
// Proof: any([(i>=0x80) == (int(hex(i).replace('0x', '').zfill(2)[0], 16)>=8) for i in range(0, 256)]) | ||
// Padding done by numberToHex | ||
return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s; | ||
} | ||
function parseDERInt(data) { | ||
if (data.length < 2 || data[0] !== 0x02) { | ||
throw new DERError(`Invalid signature integer tag: ${(0, utils_js_1.bytesToHex)(data)}`); | ||
} | ||
const len = data[1]; | ||
const res = data.subarray(2, len + 2); | ||
if (!len || res.length !== len) { | ||
throw new DERError(`Invalid signature integer: wrong length`); | ||
} | ||
// Strange condition, its not about length, but about first bytes of number. | ||
if (res[0] === 0x00 && res[1] <= 0x7f) { | ||
throw new DERError('Invalid signature integer: trailing length'); | ||
} | ||
return { data: (0, utils_js_1.bytesToNumberBE)(res), left: data.subarray(len + 2) }; | ||
} | ||
function parseDERSignature(data) { | ||
if (data.length < 2 || data[0] != 0x30) { | ||
throw new DERError(`Invalid signature tag: ${(0, utils_js_1.bytesToHex)(data)}`); | ||
} | ||
if (data[1] !== data.length - 2) { | ||
throw new DERError('Invalid signature: incorrect length'); | ||
} | ||
const { data: r, left: sBytes } = parseDERInt(data.subarray(2)); | ||
const { data: s, left: rBytesLeft } = parseDERInt(sBytes); | ||
if (rBytesLeft.length) { | ||
throw new DERError(`Invalid signature: left bytes after parsing: ${(0, utils_js_1.bytesToHex)(rBytesLeft)}`); | ||
} | ||
return { r, s }; | ||
} | ||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n | ||
const _0n = BigInt(0); | ||
const _1n = BigInt(1); | ||
const _3n = BigInt(3); | ||
const DER = { | ||
slice(s) { | ||
// Proof: any([(i>=0x80) == (int(hex(i).replace('0x', '').zfill(2)[0], 16)>=8) for i in range(0, 256)]) | ||
// Padding done by numberToHex | ||
return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s; | ||
}, | ||
parseInt(data) { | ||
if (data.length < 2 || data[0] !== 0x02) { | ||
throw new DERError(`Invalid signature integer tag: ${(0, utils_js_1.bytesToHex)(data)}`); | ||
} | ||
const len = data[1]; | ||
const res = data.subarray(2, len + 2); | ||
if (!len || res.length !== len) { | ||
throw new DERError(`Invalid signature integer: wrong length`); | ||
} | ||
// Strange condition, its not about length, but about first bytes of number. | ||
if (res[0] === 0x00 && res[1] <= 0x7f) { | ||
throw new DERError('Invalid signature integer: trailing length'); | ||
} | ||
return { data: ut.bytesToNumberBE(res), left: data.subarray(len + 2) }; | ||
}, | ||
parseSig(data) { | ||
if (data.length < 2 || data[0] != 0x30) { | ||
throw new DERError(`Invalid signature tag: ${(0, utils_js_1.bytesToHex)(data)}`); | ||
} | ||
if (data[1] !== data.length - 2) { | ||
throw new DERError('Invalid signature: incorrect length'); | ||
} | ||
const { data: r, left: sBytes } = DER.parseInt(data.subarray(2)); | ||
const { data: s, left: rBytesLeft } = DER.parseInt(sBytes); | ||
if (rBytesLeft.length) { | ||
throw new DERError(`Invalid signature: left bytes after parsing: ${(0, utils_js_1.bytesToHex)(rBytesLeft)}`); | ||
} | ||
return { r, s }; | ||
}, | ||
}; | ||
function validatePointOpts(curve) { | ||
const opts = utils.validateOpts(curve); | ||
const opts = ut.validateOpts(curve); | ||
const Fp = opts.Fp; | ||
@@ -99,17 +96,9 @@ for (const i of ['a', 'b']) { | ||
} | ||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n | ||
const _0n = BigInt(0); | ||
const _1n = BigInt(1); | ||
const _3n = BigInt(3); | ||
function weierstrassPoints(opts) { | ||
const CURVE = validatePointOpts(opts); | ||
const Fp = CURVE.Fp; | ||
// Lengths | ||
// All curves has same field / group length as for now, but it can be different for other curves | ||
const { nByteLength, nBitLength } = CURVE; | ||
const groupLen = nByteLength; | ||
// Not using ** operator with bigints for old engines. | ||
// 2n ** (8n * 32n) == 2n << (8n * 32n - 1n) | ||
//const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n); | ||
// function numToFieldStr(num: bigint): string { | ||
// if (typeof num !== 'bigint') throw new Error('Expected bigint'); | ||
// if (!(_0n <= num && num < FIELD_MASK)) throw new Error(`Expected number < 2^${fieldLen * 8}`); | ||
// return num.toString(16).padStart(2 * fieldLen, '0'); | ||
// } | ||
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ | ||
/** | ||
@@ -125,14 +114,22 @@ * y² = x³ + ax + b: Short weierstrass curve formula | ||
} | ||
// Valid group elements reside in range 1..n-1 | ||
function isWithinCurveOrder(num) { | ||
return _0n < num && num < CURVE.n; | ||
} | ||
/** | ||
* 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` | ||
*/ | ||
function normalizePrivateKey(key) { | ||
if (typeof CURVE.normalizePrivateKey === 'function') { | ||
key = CURVE.normalizePrivateKey(key); | ||
} | ||
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n: order } = CURVE; | ||
if (typeof custom === 'function') | ||
key = custom(key); | ||
let num; | ||
if (typeof key === 'bigint') { | ||
// Curve order check is done below | ||
num = key; | ||
} | ||
else if (typeof key === 'number' && Number.isSafeInteger(key) && key > 0) { | ||
else if (ut.isPositiveInt(key)) { | ||
num = BigInt(key); | ||
@@ -143,3 +140,4 @@ } | ||
throw new Error(`Expected ${groupLen} bytes of private key`); | ||
num = (0, utils_js_1.hexToNumber)(key); | ||
// Validates individual octets | ||
num = ut.hexToNumber(key); | ||
} | ||
@@ -149,3 +147,3 @@ else if (key instanceof Uint8Array) { | ||
throw new Error(`Expected ${groupLen} bytes of private key`); | ||
num = (0, utils_js_1.bytesToNumberBE)(key); | ||
num = ut.bytesToNumberBE(key); | ||
} | ||
@@ -155,4 +153,5 @@ else { | ||
} | ||
if (CURVE.wrapPrivateKey) | ||
num = mod.mod(num, CURVE.n); | ||
// Useful for curves with cofactor != 1 | ||
if (wrapPrivateKey) | ||
num = mod.mod(num, order); | ||
if (!isWithinCurveOrder(num)) | ||
@@ -162,4 +161,8 @@ throw new Error('Expected private key: 0 < key < n'); | ||
} | ||
/** | ||
* Validates if a scalar ("private number") is valid. | ||
* Scalars are valid only if they are less than curve order. | ||
*/ | ||
function normalizeScalar(num) { | ||
if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0) | ||
if (ut.isPositiveInt(num)) | ||
return BigInt(num); | ||
@@ -192,3 +195,3 @@ if (typeof num === 'bigint' && isWithinCurveOrder(num)) | ||
* Takes a bunch of Projective Points but executes only one | ||
* invert on all of them. invert is very slow operation, | ||
* inversion on all of them. Inversion is very slow operation, | ||
* so this improves performance massively. | ||
@@ -200,2 +203,5 @@ */ | ||
} | ||
/** | ||
* Optimization: converts a list of projective points to a list of identical points with Z=1. | ||
*/ | ||
static normalizeZ(points) { | ||
@@ -221,5 +227,2 @@ return ProjectivePoint.toAffineBatch(points).map(ProjectivePoint.fromAffine); | ||
} | ||
doubleAdd() { | ||
return this.add(this); | ||
} | ||
// Renes-Costello-Batina exception-free doubling formula. | ||
@@ -429,16 +432,15 @@ // There is 30% faster Jacobian formula, but it is not complete. | ||
isTorsionFree() { | ||
if (CURVE.h === _1n) | ||
return true; // No subgroups, always torsion fee | ||
if (CURVE.isTorsionFree) | ||
return CURVE.isTorsionFree(ProjectivePoint, this); | ||
// is multiplyUnsafe(CURVE.n) is always ok, same as for edwards? | ||
throw new Error('Unsupported!'); | ||
const { h: cofactor, isTorsionFree } = CURVE; | ||
if (cofactor === _1n) | ||
return true; // No subgroups, always torsion-free | ||
if (isTorsionFree) | ||
return isTorsionFree(ProjectivePoint, this); | ||
throw new Error('isTorsionFree() has not been declared for the elliptic curve'); | ||
} | ||
// Clear cofactor of G1 | ||
// https://eprint.iacr.org/2019/403 | ||
clearCofactor() { | ||
if (CURVE.h === _1n) | ||
const { h: cofactor, clearCofactor } = CURVE; | ||
if (cofactor === _1n) | ||
return this; // Fast-path | ||
if (CURVE.clearCofactor) | ||
return CURVE.clearCofactor(ProjectivePoint, this); | ||
if (clearCofactor) | ||
return clearCofactor(ProjectivePoint, this); | ||
return this.multiplyUnsafe(CURVE.h); | ||
@@ -449,3 +451,4 @@ } | ||
ProjectivePoint.ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO); | ||
const wnaf = (0, group_js_1.wNAF)(ProjectivePoint, CURVE.endo ? nBitLength / 2 : nBitLength); | ||
const _bits = CURVE.nBitLength; | ||
const wnaf = (0, group_js_1.wNAF)(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits); | ||
function assertPrjPoint(other) { | ||
@@ -481,3 +484,3 @@ if (!(other instanceof ProjectivePoint)) | ||
static fromHex(hex) { | ||
const { x, y } = CURVE.fromBytes((0, utils_js_1.ensureBytes)(hex)); | ||
const { x, y } = CURVE.fromBytes(ut.ensureBytes(hex)); | ||
const point = new Point(x, y); | ||
@@ -504,3 +507,3 @@ point.assertValidity(); | ||
return; | ||
throw new Error('Point is infinity'); | ||
throw new Error('Point at infinity'); | ||
} | ||
@@ -510,9 +513,9 @@ // Some 3rd-party test vectors require different wording between here & `fromCompressedHex` | ||
const { x, y } = this; | ||
// Check if x, y are valid field elements | ||
if (!Fp.isValid(x) || !Fp.isValid(y)) | ||
throw new Error(msg); | ||
const left = Fp.square(y); | ||
const right = weierstrassEquation(x); | ||
const left = Fp.square(y); // y² | ||
const right = weierstrassEquation(x); // x³ + ax + b | ||
if (!Fp.equals(left, right)) | ||
throw new Error(msg); | ||
// TODO: flag to disable this? | ||
if (!this.isTorsionFree()) | ||
@@ -530,11 +533,12 @@ throw new Error('Point must be of prime-order subgroup'); | ||
} | ||
toProj() { | ||
return ProjectivePoint.fromAffine(this); | ||
} | ||
// Adds point to itself | ||
double() { | ||
return ProjectivePoint.fromAffine(this).double().toAffine(); | ||
return this.toProj().double().toAffine(); | ||
} | ||
// Adds point to other point | ||
add(other) { | ||
return ProjectivePoint.fromAffine(this).add(ProjectivePoint.fromAffine(other)).toAffine(); | ||
return this.toProj().add(ProjectivePoint.fromAffine(other)).toAffine(); | ||
} | ||
// Subtracts other point from the point | ||
subtract(other) { | ||
@@ -544,12 +548,12 @@ return this.add(other.negate()); | ||
multiply(scalar) { | ||
return ProjectivePoint.fromAffine(this).multiply(scalar, this).toAffine(); | ||
return this.toProj().multiply(scalar, this).toAffine(); | ||
} | ||
multiplyUnsafe(scalar) { | ||
return ProjectivePoint.fromAffine(this).multiplyUnsafe(scalar).toAffine(); | ||
return this.toProj().multiplyUnsafe(scalar).toAffine(); | ||
} | ||
clearCofactor() { | ||
return ProjectivePoint.fromAffine(this).clearCofactor().toAffine(); | ||
return this.toProj().clearCofactor().toAffine(); | ||
} | ||
isTorsionFree() { | ||
return ProjectivePoint.fromAffine(this).isTorsionFree(); | ||
return this.toProj().isTorsionFree(); | ||
} | ||
@@ -563,3 +567,3 @@ /** | ||
multiplyAndAddUnsafe(Q, a, b) { | ||
const P = ProjectivePoint.fromAffine(this); | ||
const P = this.toProj(); | ||
const aP = a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a); | ||
@@ -573,18 +577,19 @@ const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b); | ||
static hashToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = (0, utils_js_1.ensureBytes)(msg); | ||
const { mapToCurve } = CURVE; | ||
if (!mapToCurve) | ||
throw new Error('CURVE.mapToCurve() has not been defined'); | ||
msg = ut.ensureBytes(msg); | ||
const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 2, { ...CURVE.htfDefaults, ...options }); | ||
const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]); | ||
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]); | ||
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor(); | ||
return p; | ||
const { x: x0, y: y0 } = mapToCurve(u[0]); | ||
const { x: x1, y: y1 } = mapToCurve(u[1]); | ||
return new Point(x0, y0).add(new Point(x1, y1)).clearCofactor(); | ||
} | ||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3 | ||
static encodeToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = (0, utils_js_1.ensureBytes)(msg); | ||
const { mapToCurve } = CURVE; | ||
if (!mapToCurve) | ||
throw new Error('CURVE.mapToCurve() has not been defined'); | ||
msg = ut.ensureBytes(msg); | ||
const u = (0, hash_to_curve_js_1.hash_to_field)(msg, 1, { ...CURVE.htfDefaults, ...options }); | ||
const { x, y } = CURVE.mapToCurve(u[0]); | ||
const { x, y } = mapToCurve(u[0]); | ||
return new Point(x, y).clearCofactor(); | ||
@@ -594,7 +599,7 @@ } | ||
/** | ||
* Base point aka generator. public_key = Point.BASE * private_key | ||
* Base point aka generator. Any public_key = Point.BASE * private_key | ||
*/ | ||
Point.BASE = new Point(CURVE.Gx, CURVE.Gy); | ||
/** | ||
* Identity point aka point at infinity. point = point + zero_point | ||
* Identity point aka point at infinity. p - p = zero_p; p + zero_p = p | ||
*/ | ||
@@ -612,4 +617,4 @@ Point.ZERO = new Point(Fp.ZERO, Fp.ZERO); | ||
function validateOpts(curve) { | ||
const opts = utils.validateOpts(curve); | ||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen)) | ||
const opts = ut.validateOpts(curve); | ||
if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen)) | ||
throw new Error('Invalid hash function'); | ||
@@ -670,3 +675,3 @@ if (typeof opts.hmac !== 'function') | ||
} | ||
return (0, utils_js_1.concatBytes)(...out); | ||
return ut.concatBytes(...out); | ||
} | ||
@@ -678,4 +683,4 @@ } | ||
const Fp = CURVE.Fp; | ||
const compressedLen = Fp.BYTES + 1; // 33 | ||
const uncompressedLen = 2 * Fp.BYTES + 1; // 65 | ||
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32 | ||
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32 | ||
function isValidFieldElement(num) { | ||
@@ -688,7 +693,9 @@ // 0 is disallowed by arbitrary reasons. Probably because infinity point? | ||
toBytes(c, point, isCompressed) { | ||
const x = Fp.toBytes(point.x); | ||
const cat = ut.concatBytes; | ||
if (isCompressed) { | ||
return (0, utils_js_1.concatBytes)(new Uint8Array([point.hasEvenY() ? 0x02 : 0x03]), Fp.toBytes(point.x)); | ||
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x); | ||
} | ||
else { | ||
return (0, utils_js_1.concatBytes)(new Uint8Array([0x04]), Fp.toBytes(point.x), Fp.toBytes(point.y)); | ||
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(point.y)); | ||
} | ||
@@ -701,3 +708,3 @@ }, | ||
if (len === compressedLen && (header === 0x02 || header === 0x03)) { | ||
const x = (0, utils_js_1.bytesToNumberBE)(bytes.subarray(1)); | ||
const x = ut.bytesToNumberBE(bytes.subarray(1)); | ||
if (!isValidFieldElement(x)) | ||
@@ -755,13 +762,14 @@ throw new Error('Point is not on curve'); | ||
} | ||
function bits2int_2(bytes) { | ||
const delta = bytes.length * 8 - CURVE.nBitLength; | ||
const num = ut.bytesToNumberBE(bytes); | ||
return delta > 0 ? num >> BigInt(delta) : num; | ||
} | ||
// Ensures ECDSA message hashes are 32 bytes and < curve order | ||
function _truncateHash(hash, truncateOnly = false) { | ||
const { n, nBitLength } = CURVE; | ||
const byteLength = hash.length; | ||
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits) | ||
let h = (0, utils_js_1.bytesToNumberBE)(hash); | ||
if (delta > 0) | ||
h = h >> BigInt(delta); | ||
if (!truncateOnly && h >= n) | ||
h -= n; | ||
return h; | ||
const h = bits2int_2(hash); | ||
if (truncateOnly) | ||
return h; | ||
const { n } = CURVE; | ||
return h >= n ? h - n : h; | ||
} | ||
@@ -779,3 +787,3 @@ const truncateHash = CURVE.truncateHash || _truncateHash; | ||
} | ||
// pair (32 bytes of r, 32 bytes of s) | ||
// pair (bytes of r, bytes of s) | ||
static fromCompact(hex) { | ||
@@ -787,5 +795,7 @@ const arr = hex instanceof Uint8Array; | ||
const str = arr ? (0, utils_js_1.bytesToHex)(hex) : hex; | ||
if (str.length !== 128) | ||
throw new Error(`${name}: Expected 64-byte hex`); | ||
return new Signature((0, utils_js_1.hexToNumber)(str.slice(0, 64)), (0, utils_js_1.hexToNumber)(str.slice(64, 128))); | ||
const gl = CURVE.nByteLength * 2; // group length in hex, not ui8a | ||
if (str.length !== 2 * gl) | ||
throw new Error(`${name}: Expected ${gl / 2}-byte hex`); | ||
const slice = (from, to) => ut.hexToNumber(str.slice(from, to)); | ||
return new Signature(slice(0, gl), slice(gl, 2 * gl)); | ||
} | ||
@@ -798,3 +808,3 @@ // DER encoded ECDSA signature | ||
throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`); | ||
const { r, s } = parseDERSignature(arr ? hex : (0, utils_js_1.hexToBytes)(hex)); | ||
const { r, s } = DER.parseSig(arr ? hex : ut.hexToBytes(hex)); | ||
return new Signature(r, s); | ||
@@ -815,2 +825,3 @@ } | ||
* https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Public_key_recovery | ||
* It's also possible to recover key without bit: try all 4 bit values and check for sig match. | ||
* | ||
@@ -831,7 +842,10 @@ * ``` | ||
throw new Error('Cannot recover: recovery bit is not present'); | ||
if (recovery !== 0 && recovery !== 1) | ||
if (![0, 1, 2, 3].includes(recovery)) | ||
throw new Error('Cannot recover: invalid recovery bit'); | ||
const h = truncateHash((0, utils_js_1.ensureBytes)(msgHash)); | ||
const h = truncateHash(ut.ensureBytes(msgHash)); | ||
const { n } = CURVE; | ||
const rinv = mod.invert(r, n); | ||
const radj = recovery === 2 || recovery === 3 ? r + n : r; | ||
if (radj >= Fp.ORDER) | ||
throw new Error('Cannot recover: bit 2/3 is invalid with current r'); | ||
const rinv = mod.invert(radj, n); | ||
// Q = u1⋅G + u2⋅R | ||
@@ -841,4 +855,4 @@ const u1 = mod.mod(-h * rinv, n); | ||
const prefix = recovery & 1 ? '03' : '02'; | ||
const R = Point.fromHex(prefix + numToFieldStr(r)); | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); | ||
const R = Point.fromHex(prefix + numToFieldStr(radj)); | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // unsafe is fine: no priv data leaked | ||
if (!Q) | ||
@@ -863,18 +877,19 @@ throw new Error('Cannot recover: point at infinify'); | ||
// DER-encoded | ||
toDERRawBytes(isCompressed = false) { | ||
return (0, utils_js_1.hexToBytes)(this.toDERHex(isCompressed)); | ||
toDERRawBytes() { | ||
return ut.hexToBytes(this.toDERHex()); | ||
} | ||
toDERHex(isCompressed = false) { | ||
const sHex = sliceDER((0, utils_js_1.numberToHexUnpadded)(this.s)); | ||
if (isCompressed) | ||
return sHex; | ||
const rHex = sliceDER((0, utils_js_1.numberToHexUnpadded)(this.r)); | ||
const rLen = (0, utils_js_1.numberToHexUnpadded)(rHex.length / 2); | ||
const sLen = (0, utils_js_1.numberToHexUnpadded)(sHex.length / 2); | ||
const length = (0, utils_js_1.numberToHexUnpadded)(rHex.length / 2 + sHex.length / 2 + 4); | ||
toDERHex() { | ||
const { numberToHexUnpadded: toHex } = ut; | ||
const sHex = DER.slice(toHex(this.s)); | ||
const rHex = DER.slice(toHex(this.r)); | ||
const sHexL = sHex.length / 2; | ||
const rHexL = rHex.length / 2; | ||
const sLen = toHex(sHexL); | ||
const rLen = toHex(rHexL); | ||
const length = toHex(rHexL + sHexL + 4); | ||
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`; | ||
} | ||
// 32 bytes of r, then 32 bytes of s | ||
// padded bytes of r, then padded bytes of s | ||
toCompactRawBytes() { | ||
return (0, utils_js_1.hexToBytes)(this.toCompactHex()); | ||
return ut.hexToBytes(this.toCompactHex()); | ||
} | ||
@@ -886,4 +901,2 @@ toCompactHex() { | ||
const utils = { | ||
mod: (n, modulo = Fp.ORDER) => mod.mod(n, modulo), | ||
invert: Fp.invert, | ||
isValidPrivateKey(privateKey) { | ||
@@ -908,3 +921,3 @@ try { | ||
*/ | ||
hashToPrivateKey: (hash) => numToField((0, utils_js_1.hashToPrivateScalar)(hash, CURVE_ORDER)), | ||
hashToPrivateKey: (hash) => numToField(ut.hashToPrivateScalar(hash, CURVE_ORDER)), | ||
/** | ||
@@ -931,6 +944,6 @@ * Produces cryptographically secure private key from random of size (nBitLength+64) | ||
/** | ||
* Computes public key for a private key. | ||
* Computes public key for a private key. Checks for validity of the private key. | ||
* @param privateKey private key | ||
* @param isCompressed whether to return compact, or full key | ||
* @returns Public key, full by default; short when isCompressed=true | ||
* @param isCompressed whether to return compact (default), or full key | ||
* @returns Public key, full when isCompressed=false; short when isCompressed=true | ||
*/ | ||
@@ -956,8 +969,8 @@ function getPublicKey(privateKey, isCompressed = false) { | ||
/** | ||
* ECDH (Elliptic Curve Diffie Hellman) implementation. | ||
* 1. Checks for validity of private key | ||
* 2. Checks for the public key of being on-curve | ||
* ECDH (Elliptic Curve Diffie Hellman). | ||
* Computes shared public key from private key and public key. | ||
* Checks: 1) private key validity 2) shared key is on-curve | ||
* @param privateA private key | ||
* @param publicB different public key | ||
* @param isCompressed whether to return compact (33-byte), or full (65-byte) key | ||
* @param isCompressed whether to return compact (default), or full key | ||
* @returns shared public key | ||
@@ -976,4 +989,17 @@ */ | ||
function bits2int(bytes) { | ||
const slice = bytes.length > Fp.BYTES ? bytes.slice(0, Fp.BYTES) : bytes; | ||
return (0, utils_js_1.bytesToNumberBE)(slice); | ||
const { nByteLength } = CURVE; | ||
if (!(bytes instanceof Uint8Array)) | ||
throw new Error('Expected Uint8Array'); | ||
const slice = bytes.length > nByteLength ? bytes.slice(0, nByteLength) : bytes; | ||
// const slice = bytes; nByteLength; nBitLength; | ||
let num = ut.bytesToNumberBE(slice); | ||
// const { nBitLength } = CURVE; | ||
// const delta = (bytes.length * 8) - nBitLength; | ||
// if (delta > 0) { | ||
// // console.log('bits=', bytes.length*8, 'CURVE n=', nBitLength, 'delta=', delta); | ||
// // console.log(bytes.length, nBitLength, delta); | ||
// // console.log(bytes, new Error().stack); | ||
// num >>= BigInt(delta); | ||
// } | ||
return num; | ||
} | ||
@@ -986,3 +1012,3 @@ function bits2octets(bytes) { | ||
function int2octets(num) { | ||
return numToField(num); // prohibits >32 bytes | ||
return numToField(num); // prohibits >nByteLength bytes | ||
} | ||
@@ -995,3 +1021,3 @@ // Steps A, D of RFC6979 3.2 | ||
// Step A is ignored, since we already provide hash instead of msg | ||
const h1 = numToField(truncateHash((0, utils_js_1.ensureBytes)(msgHash))); | ||
const h1 = numToField(truncateHash(ut.ensureBytes(msgHash))); | ||
const d = normalizePrivateKey(privateKey); | ||
@@ -1004,3 +1030,3 @@ // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k') | ||
extraEntropy = CURVE.randomBytes(Fp.BYTES); | ||
const e = (0, utils_js_1.ensureBytes)(extraEntropy); | ||
const e = ut.ensureBytes(extraEntropy); | ||
if (e.length !== Fp.BYTES) | ||
@@ -1013,3 +1039,3 @@ throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`); | ||
// V, 0x00 are done in HmacDRBG constructor. | ||
const seed = (0, utils_js_1.concatBytes)(...seedArgs); | ||
const seed = ut.concatBytes(...seedArgs); | ||
const m = bits2int(h1); | ||
@@ -1038,6 +1064,7 @@ return { seed, m, d }; | ||
return; | ||
// s = (1/k * (m + dr) mod n | ||
// s = (m + dr)/k mod n where x/k == x*inv(k) | ||
const s = mod.mod(kinv * mod.mod(m + mod.mod(d * r, n), n), n); | ||
if (s === _0n) | ||
return; | ||
// recovery bit is usually 0 or 1; rarely it's 2 or 3, when q.x > n | ||
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); | ||
@@ -1051,7 +1078,14 @@ let normS = s; | ||
} | ||
const defaultSigOpts = { lowS: CURVE.lowS }; | ||
/** | ||
* Signs message hash (not message: you need to hash it by yourself). | ||
* ``` | ||
* sign(m, d, k) where | ||
* (x, y) = G × k | ||
* r = x mod n | ||
* s = (m + dr)/k mod n | ||
* ``` | ||
* @param opts `lowS, extraEntropy` | ||
*/ | ||
function sign(msgHash, privKey, opts = { lowS: CURVE.lowS }) { | ||
function sign(msgHash, privKey, opts = defaultSigOpts) { | ||
// Steps A, D of RFC6979 3.2. | ||
@@ -1068,2 +1102,8 @@ const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
} | ||
/** | ||
* Signs a message (not message hash). | ||
*/ | ||
function signUnhashed(msg, privKey, opts = defaultSigOpts) { | ||
return sign(CURVE.hash(ut.ensureBytes(msg)), privKey, opts); | ||
} | ||
// Enable precomputes. Slows down first publicKey computation by 20ms. | ||
@@ -1101,3 +1141,3 @@ Point.BASE._setWindowSize(8); | ||
} | ||
msgHash = (0, utils_js_1.ensureBytes)(msgHash); | ||
msgHash = ut.ensureBytes(msgHash); | ||
} | ||
@@ -1136,2 +1176,3 @@ catch (error) { | ||
sign, | ||
signUnhashed, | ||
verify, | ||
@@ -1138,0 +1179,0 @@ Point, |
@@ -0,1 +1,2 @@ | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import { CurveFn } from './abstract/bls.js'; | ||
@@ -2,0 +3,0 @@ import * as mod from './abstract/modular.js'; |
"use strict"; | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.bls12_381 = void 0; | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// The pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to: | ||
// - Construct zk-SNARKs at the 128-bit security | ||
// - Use threshold signatures, which allows a user to sign lots of messages with one signature and verify them swiftly in a batch, using Boneh-Lynn-Shacham signature scheme. | ||
// Differences from @noble/bls12-381 1.4: | ||
// - PointG1 -> G1.Point | ||
// - PointG2 -> G2.Point | ||
// - PointG2.fromSignature -> Signature.decode | ||
// - PointG2.toSignature -> Signature.encode | ||
// - Fixed Fp2 ORDER | ||
// - Points now have only two coordinates | ||
const sha256_1 = require("@noble/hashes/sha256"); | ||
@@ -13,9 +23,2 @@ const utils_1 = require("@noble/hashes/utils"); | ||
const hash_to_curve_js_1 = require("./abstract/hash-to-curve.js"); | ||
// Differences from bls12-381: | ||
// - PointG1 -> G1.Point | ||
// - PointG2 -> G2.Point | ||
// - PointG2.fromSignature -> Signature.decode | ||
// - PointG2.toSignature -> Signature.encode | ||
// - Fixed Fp2 ORDER | ||
// Points now have only two coordinates | ||
// CURVE FIELDS | ||
@@ -104,2 +107,4 @@ // Finite field over p. | ||
sqrt: (num) => { | ||
if (Fp2.equals(num, Fp2.ZERO)) | ||
return Fp2.ZERO; // Algo doesn't handles this case | ||
// TODO: Optimize this line. It's extremely slow. | ||
@@ -106,0 +111,0 @@ // Speeding this up would boost aggregateSignatures. |
@@ -228,3 +228,3 @@ "use strict"; | ||
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); | ||
const bytes255ToNumberLE = (bytes) => exports.ed25519.utils.mod((0, utils_js_1.bytesToNumberLE)(bytes) & MAX_255B); | ||
const bytes255ToNumberLE = (bytes) => exports.ed25519.CURVE.Fp.create((0, utils_js_1.bytesToNumberLE)(bytes) & MAX_255B); | ||
// Computes Elligator map for Ristretto | ||
@@ -235,3 +235,3 @@ // https://ristretto.group/formulas/elligator.html | ||
const P = exports.ed25519.CURVE.Fp.ORDER; | ||
const { mod } = exports.ed25519.utils; | ||
const mod = exports.ed25519.CURVE.Fp.create; | ||
const r = mod(SQRT_M1 * r0 * r0); // 1 | ||
@@ -294,3 +294,3 @@ const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2 | ||
const P = exports.ed25519.CURVE.Fp.ORDER; | ||
const { mod } = exports.ed25519.utils; | ||
const mod = exports.ed25519.CURVE.Fp.create; | ||
const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint'; | ||
@@ -327,3 +327,3 @@ const s = bytes255ToNumberLE(hex); | ||
const P = exports.ed25519.CURVE.Fp.ORDER; | ||
const { mod } = exports.ed25519.utils; | ||
const mod = exports.ed25519.CURVE.Fp.create; | ||
const u1 = mod(mod(z + y) * mod(z - y)); // 1 | ||
@@ -366,3 +366,3 @@ const u2 = mod(x * y); // 2 | ||
const b = other.ep; | ||
const { mod } = exports.ed25519.utils; | ||
const mod = exports.ed25519.CURVE.Fp.create; | ||
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2) | ||
@@ -369,0 +369,0 @@ const one = mod(a.x * b.y) === mod(a.y * b.x); |
@@ -132,3 +132,4 @@ "use strict"; | ||
Fp, | ||
// Subgroup order: how many points ed448 has; 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n | ||
// Subgroup order: how many points curve has; | ||
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n | ||
n: BigInt('181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'), | ||
@@ -135,0 +136,0 @@ nBitLength: 456, |
@@ -1,19 +0,9 @@ | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Barreto-Lynn-Scott Curves. A family of pairing friendly curves, with embedding degree = 12 or 24 | ||
// NOTE: only 12 supported for now | ||
// Constructed from pair of weierstrass curves, based pairing logic | ||
import * as mod from './modular.js'; | ||
import { ensureBytes, numberToBytesBE, bytesToNumberBE, bitLen, bitGet } from './utils.js'; | ||
// Types | ||
import { hexToBytes, bytesToHex } from './utils.js'; | ||
import { stringToBytes, hash_to_field, expand_message_xmd } from './hash-to-curve.js'; | ||
import * as ut from './utils.js'; | ||
import { stringToBytes, hash_to_field as hashToField, expand_message_xmd as expandMessageXMD, } from './hash-to-curve.js'; | ||
import { weierstrassPoints } from './weierstrass.js'; | ||
export function bls(CURVE) { | ||
// Fields looks pretty specific for curve, so for now we need to pass them with options | ||
const Fp = CURVE.Fp; | ||
const Fr = CURVE.Fr; | ||
const Fp2 = CURVE.Fp2; | ||
const Fp6 = CURVE.Fp6; | ||
const Fp12 = CURVE.Fp12; | ||
const BLS_X_LEN = bitLen(CURVE.x); | ||
const { Fp, Fr, Fp2, Fp6, Fp12 } = CURVE; | ||
const BLS_X_LEN = ut.bitLen(CURVE.x); | ||
const groupLen = 32; // TODO: calculate; hardcoded for now | ||
// Pre-compute coefficients for sparse multiplication | ||
@@ -42,3 +32,3 @@ // Point addition and point double calculations is reused for coefficients | ||
Rz = Fp2.mul(t0, t4); // T0 * T4 | ||
if (bitGet(CURVE.x, i)) { | ||
if (ut.bitGet(CURVE.x, i)) { | ||
// Addition | ||
@@ -64,2 +54,3 @@ let t0 = Fp2.sub(Ry, Fp2.mul(Qy, Rz)); // Ry - Qy * Rz | ||
function millerLoop(ell, g1) { | ||
const { x } = CURVE; | ||
const Px = g1[0]; | ||
@@ -71,3 +62,3 @@ const Py = g1[1]; | ||
f12 = Fp12.multiplyBy014(f12, E[0], Fp2.mul(E[1], Px), Fp2.mul(E[2], Py)); | ||
if (bitGet(CURVE.x, i)) { | ||
if (ut.bitGet(x, i)) { | ||
j += 1; | ||
@@ -82,50 +73,13 @@ const F = ell[j]; | ||
} | ||
// bls12-381 is a construction of two curves: | ||
// 1. Fp: (x, y) | ||
// 2. Fp₂: ((x₁, x₂+i), (y₁, y₂+i)) - (complex numbers) | ||
// | ||
// Bilinear Pairing (ate pairing) is used to combine both elements into a paired one: | ||
// Fp₁₂ = e(Fp, Fp2) | ||
// where Fp₁₂ = 12-degree polynomial | ||
// Pairing is used to verify signatures. | ||
// | ||
// We are using Fp for private keys (shorter) and Fp2 for signatures (longer). | ||
// Some projects may prefer to swap this relation, it is not supported for now. | ||
const htfDefaults = { ...CURVE.htfDefaults }; | ||
function isWithinCurveOrder(num) { | ||
return 0 < num && num < CURVE.r; | ||
} | ||
const utils = { | ||
hexToBytes: hexToBytes, | ||
bytesToHex: bytesToHex, | ||
mod: mod.mod, | ||
stringToBytes, | ||
hexToBytes: ut.hexToBytes, | ||
bytesToHex: ut.bytesToHex, | ||
stringToBytes: stringToBytes, | ||
// TODO: do we need to export it here? | ||
hashToField: (msg, count, options = {}) => hash_to_field(msg, count, { ...CURVE.htfDefaults, ...options }), | ||
expandMessageXMD: (msg, DST, lenInBytes, H = CURVE.hash) => expand_message_xmd(msg, DST, lenInBytes, H), | ||
/** | ||
* Can take 40 or more bytes of uniform input e.g. from CSPRNG or KDF | ||
* and convert them into private key, with the modulo bias being negligible. | ||
* As per FIPS 186 B.1.1. | ||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | ||
* @param hash hash output from sha512, or a similar function | ||
* @returns valid private key | ||
*/ | ||
hashToPrivateKey: (hash) => { | ||
hash = ensureBytes(hash); | ||
if (hash.length < 40 || hash.length > 1024) | ||
throw new Error('Expected 40-1024 bytes of private key as per FIPS 186'); | ||
// hashToPrivateScalar(hash, CURVE.r) | ||
// NOTE: doesn't add +/-1 | ||
const num = mod.mod(bytesToNumberBE(hash), CURVE.r); | ||
// This should never happen | ||
if (num === 0n || num === 1n) | ||
throw new Error('Invalid private key'); | ||
return numberToBytesBE(num, 32); | ||
}, | ||
randomBytes: (bytesLength = 32) => CURVE.randomBytes(bytesLength), | ||
// NIST SP 800-56A rev 3, section 5.6.1.2.2 | ||
// https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | ||
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(40)), | ||
getDSTLabel: () => htfDefaults.DST, | ||
hashToField: (msg, count, options = {}) => hashToField(msg, count, { ...CURVE.htfDefaults, ...options }), | ||
expandMessageXMD: (msg, DST, lenInBytes, H = CURVE.hash) => expandMessageXMD(msg, DST, lenInBytes, H), | ||
hashToPrivateKey: (hash) => Fr.toBytes(ut.hashToPrivateScalar(hash, CURVE.r)), | ||
randomBytes: (bytesLength = groupLen) => CURVE.randomBytes(bytesLength), | ||
randomPrivateKey: () => utils.hashToPrivateKey(utils.randomBytes(groupLen + 8)), | ||
getDSTLabel: () => CURVE.htfDefaults.DST, | ||
setDSTLabel(newLabel) { | ||
@@ -136,22 +90,5 @@ // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3.1 | ||
} | ||
htfDefaults.DST = newLabel; | ||
CURVE.htfDefaults.DST = newLabel; | ||
}, | ||
}; | ||
function normalizePrivKey(key) { | ||
let int; | ||
if (key instanceof Uint8Array && key.length === 32) | ||
int = bytesToNumberBE(key); | ||
else if (typeof key === 'string' && key.length === 64) | ||
int = BigInt(`0x${key}`); | ||
else if (typeof key === 'number' && key > 0 && Number.isSafeInteger(key)) | ||
int = BigInt(key); | ||
else if (typeof key === 'bigint' && key > 0n) | ||
int = key; | ||
else | ||
throw new TypeError('Expected valid private key'); | ||
int = mod.mod(int, CURVE.r); | ||
if (!isWithinCurveOrder(int)) | ||
throw new Error('Private key must be 0 < key < CURVE.r'); | ||
return int; | ||
} | ||
// Point on G1 curve: (x, y) | ||
@@ -210,3 +147,3 @@ const G1 = weierstrassPoints({ | ||
msgPoint.assertValidity(); | ||
const sigPoint = msgPoint.multiply(normalizePrivKey(privateKey)); | ||
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey)); | ||
if (message instanceof G2.Point) | ||
@@ -213,0 +150,0 @@ return sigPoint; |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Twisted Edwards curve. The formula is: ax² + y² = 1 + dx²y² | ||
// Differences from @noble/ed25519 1.7: | ||
// 1. Different field element lengths in ed448: | ||
// 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 too (unexpected), now using generalized formula | ||
// 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 | ||
import * as mod from './modular.js'; | ||
import { bytesToHex, concatBytes, ensureBytes, numberToBytesLE, bytesToNumberLE, hashToPrivateScalar, validateOpts as utilOpts, } from './utils.js'; // TODO: import * as u from './utils.js'? | ||
import * as ut from './utils.js'; | ||
import { ensureBytes } from './utils.js'; | ||
import { wNAF } from './group.js'; | ||
import { hash_to_field, validateHTFOpts } from './hash-to-curve.js'; | ||
import { hash_to_field as hashToField, validateHTFOpts } from './hash-to-curve.js'; | ||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n | ||
@@ -19,10 +20,10 @@ const _0n = BigInt(0); | ||
const _8n = BigInt(8); | ||
// Should be separate from overrides, since overrides can use information about curve (for example nBits) | ||
function validateOpts(curve) { | ||
const opts = utilOpts(curve); | ||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen)) | ||
const opts = ut.validateOpts(curve); | ||
if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen)) | ||
throw new Error('Invalid hash function'); | ||
for (const i of ['a', 'd']) { | ||
if (typeof opts[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${opts[i]} (${typeof opts[i]})`); | ||
const val = opts[i]; | ||
if (typeof val !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
@@ -33,9 +34,3 @@ for (const fn of ['randomBytes']) { | ||
} | ||
for (const fn of [ | ||
'adjustScalarBytes', | ||
'domain', | ||
'uvRatio', | ||
'mapToCurve', | ||
'clearCofactor', | ||
]) { | ||
for (const fn of ['adjustScalarBytes', 'domain', 'uvRatio', 'mapToCurve']) { | ||
if (opts[fn] === undefined) | ||
@@ -56,8 +51,3 @@ continue; // Optional | ||
const CURVE_ORDER = CURVE.n; | ||
const fieldLen = Fp.BYTES; // 32 (length of one field element) | ||
if (fieldLen > 2048) | ||
throw new Error('Field lengths over 2048 are not supported'); | ||
const groupLen = CURVE.nByteLength; | ||
// (2n ** 256n).toString(16); | ||
const maxGroupElement = _2n ** BigInt(groupLen * 8); // previous POW_2_256 | ||
const maxGroupElement = _2n ** BigInt(CURVE.nByteLength * 8); | ||
// Function overrides | ||
@@ -67,20 +57,18 @@ const { randomBytes } = CURVE; | ||
// sqrt(u/v) | ||
function _uvRatio(u, v) { | ||
try { | ||
const value = Fp.sqrt(u * Fp.invert(v)); | ||
return { isValid: true, value }; | ||
} | ||
catch (e) { | ||
return { isValid: false, value: _0n }; | ||
} | ||
} | ||
const uvRatio = CURVE.uvRatio || _uvRatio; | ||
const _adjustScalarBytes = (bytes) => bytes; // NOOP | ||
const adjustScalarBytes = CURVE.adjustScalarBytes || _adjustScalarBytes; | ||
function _domain(data, ctx, phflag) { | ||
if (ctx.length || phflag) | ||
throw new Error('Contexts/pre-hash are not supported'); | ||
return data; | ||
} | ||
const domain = CURVE.domain || _domain; // NOOP | ||
const uvRatio = CURVE.uvRatio || | ||
((u, v) => { | ||
try { | ||
return { isValid: true, value: Fp.sqrt(u * Fp.invert(v)) }; | ||
} | ||
catch (e) { | ||
return { isValid: false, value: _0n }; | ||
} | ||
}); | ||
const adjustScalarBytes = CURVE.adjustScalarBytes || ((bytes) => bytes); // NOOP | ||
const domain = CURVE.domain || | ||
((data, ctx, phflag) => { | ||
if (ctx.length || phflag) | ||
throw new Error('Contexts/pre-hash are not supported'); | ||
return data; | ||
}); // NOOP | ||
/** | ||
@@ -222,6 +210,4 @@ * Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy). | ||
// an exposed private key e.g. sig verification. | ||
// Allows scalar bigger than curve order, but less than 2^256 | ||
multiplyUnsafe(scalar) { | ||
let n = normalizeScalar(scalar, CURVE_ORDER, false); | ||
const G = ExtendedPoint.BASE; | ||
const P0 = ExtendedPoint.ZERO; | ||
@@ -232,6 +218,9 @@ if (n === _0n) | ||
return this; | ||
if (this.equals(G)) | ||
if (this.equals(ExtendedPoint.BASE)) | ||
return this.wNAF(n); | ||
return wnaf.unsafeLadder(this, n); | ||
} | ||
// Checks if point is of small order. | ||
// If you add something to small order point, you will have "dirty" | ||
// point with torsion component. | ||
// Multiplies point by cofactor and checks if the result is 0. | ||
@@ -241,5 +230,6 @@ isSmallOrder() { | ||
} | ||
// Multiplies point by a very big scalar n and checks if the result is 0. | ||
// Multiplies point by curve order (very big scalar CURVE.n) and checks if the result is 0. | ||
// Returns `false` is the point is dirty. | ||
isTorsionFree() { | ||
return this.multiplyUnsafe(CURVE_ORDER).equals(ExtendedPoint.ZERO); | ||
return wnaf.unsafeLadder(this, CURVE_ORDER).equals(ExtendedPoint.ZERO); | ||
} | ||
@@ -263,9 +253,6 @@ // Converts Extended point to default (x, y) coordinates. | ||
clearCofactor() { | ||
if (CURVE.h === _1n) | ||
return this; // Fast-path | ||
// clear_cofactor(P) := h_eff * P | ||
// hEff = h for ed25519/ed448. Maybe worth moving to params? | ||
if (CURVE.clearCofactor) | ||
return CURVE.clearCofactor(ExtendedPoint, this); | ||
return this.multiplyUnsafe(CURVE.h); | ||
const { h: cofactor } = CURVE; | ||
if (cofactor === _1n) | ||
return this; | ||
return this.multiplyUnsafe(cofactor); | ||
} | ||
@@ -275,3 +262,3 @@ } | ||
ExtendedPoint.ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n); | ||
const wnaf = wNAF(ExtendedPoint, groupLen * 8); | ||
const wnaf = wNAF(ExtendedPoint, CURVE.nByteLength * 8); | ||
function assertExtPoint(other) { | ||
@@ -300,3 +287,4 @@ if (!(other instanceof ExtendedPoint)) | ||
const { d, a } = CURVE; | ||
hex = ensureBytes(hex, fieldLen); | ||
const len = Fp.BYTES; | ||
hex = ensureBytes(hex, len); | ||
// 1. First, interpret the string as an integer in little-endian | ||
@@ -308,9 +296,9 @@ // representation. Bit 255 of this number is the least significant | ||
const normed = hex.slice(); | ||
const lastByte = hex[fieldLen - 1]; | ||
normed[fieldLen - 1] = lastByte & ~0x80; | ||
const y = bytesToNumberLE(normed); | ||
const lastByte = hex[len - 1]; | ||
normed[len - 1] = lastByte & ~0x80; | ||
const y = ut.bytesToNumberLE(normed); | ||
if (strict && y >= Fp.ORDER) | ||
throw new Error('Expected 0 < hex < P'); | ||
if (!strict && y >= maxGroupElement) | ||
throw new Error('Expected 0 < hex < 2**256'); | ||
throw new Error('Expected 0 < hex < CURVE.n'); | ||
// 2. To recover the x-coordinate, the curve equation implies | ||
@@ -347,4 +335,4 @@ // Ed25519: x² = (y² - 1) / (d y² + 1) (mod p). | ||
toRawBytes() { | ||
const bytes = numberToBytesLE(this.y, fieldLen); | ||
bytes[fieldLen - 1] |= this.x & _1n ? 0x80 : 0; | ||
const bytes = ut.numberToBytesLE(this.y, Fp.BYTES); | ||
bytes[Fp.BYTES - 1] |= this.x & _1n ? 0x80 : 0; | ||
return bytes; | ||
@@ -354,4 +342,6 @@ } | ||
toHex() { | ||
return bytesToHex(this.toRawBytes()); | ||
return ut.bytesToHex(this.toRawBytes()); | ||
} | ||
// Determines if point is in prime-order subgroup. | ||
// Returns `false` is the point is dirty. | ||
isTorsionFree() { | ||
@@ -391,8 +381,8 @@ return ExtendedPoint.fromAffine(this).isTorsionFree(); | ||
static hashToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
const { mapToCurve, htfDefaults } = CURVE; | ||
if (!mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = ensureBytes(msg); | ||
const u = hash_to_field(msg, 2, { ...CURVE.htfDefaults, ...options }); | ||
const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]); | ||
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]); | ||
const u = hashToField(ensureBytes(msg), 2, { ...htfDefaults, ...options }); | ||
const { x: x0, y: y0 } = mapToCurve(u[0]); | ||
const { x: x1, y: y1 } = mapToCurve(u[1]); | ||
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor(); | ||
@@ -403,7 +393,7 @@ return p; | ||
static encodeToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
const { mapToCurve, htfDefaults } = CURVE; | ||
if (!mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = ensureBytes(msg); | ||
const u = hash_to_field(msg, 1, { ...CURVE.htfDefaults, ...options }); | ||
const { x, y } = CURVE.mapToCurve(u[0]); | ||
const u = hashToField(ensureBytes(msg), 1, { ...htfDefaults, ...options }); | ||
const { x, y } = mapToCurve(u[0]); | ||
return new Point(x, y).clearCofactor(); | ||
@@ -428,5 +418,6 @@ } | ||
static fromHex(hex) { | ||
const bytes = ensureBytes(hex, 2 * fieldLen); | ||
const r = Point.fromHex(bytes.slice(0, fieldLen), false); | ||
const s = bytesToNumberLE(bytes.slice(fieldLen, 2 * fieldLen)); | ||
const len = Fp.BYTES; | ||
const bytes = ensureBytes(hex, 2 * len); | ||
const r = Point.fromHex(bytes.slice(0, len), false); | ||
const s = ut.bytesToNumberLE(bytes.slice(len, 2 * len)); | ||
return new Signature(r, s); | ||
@@ -443,11 +434,11 @@ } | ||
toRawBytes() { | ||
return concatBytes(this.r.toRawBytes(), numberToBytesLE(this.s, fieldLen)); | ||
return ut.concatBytes(this.r.toRawBytes(), ut.numberToBytesLE(this.s, Fp.BYTES)); | ||
} | ||
toHex() { | ||
return bytesToHex(this.toRawBytes()); | ||
return ut.bytesToHex(this.toRawBytes()); | ||
} | ||
} | ||
// Little-endian SHA512 with modulo n | ||
function modlLE(hash) { | ||
return mod.mod(bytesToNumberLE(hash), CURVE_ORDER); | ||
function modnLE(hash) { | ||
return mod.mod(ut.bytesToNumberLE(hash), CURVE_ORDER); | ||
} | ||
@@ -463,3 +454,3 @@ /** | ||
throw new TypeError('Specify max value'); | ||
if (typeof num === 'number' && Number.isSafeInteger(num)) | ||
if (ut.isPositiveInt(num)) | ||
num = BigInt(num); | ||
@@ -476,32 +467,26 @@ if (typeof num === 'bigint' && num < max) { | ||
} | ||
throw new TypeError('Expected valid scalar: 0 < scalar < max'); | ||
throw new TypeError(`Expected valid scalar: 0 < scalar < ${max}`); | ||
} | ||
function checkPrivateKey(key) { | ||
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */ | ||
function getExtendedPublicKey(key) { | ||
const groupLen = CURVE.nByteLength; | ||
// Normalize bigint / number / string to Uint8Array | ||
key = | ||
typeof key === 'bigint' || typeof key === 'number' | ||
? numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen) | ||
: ensureBytes(key); | ||
if (key.length !== groupLen) | ||
throw new Error(`Expected ${groupLen} bytes, got ${key.length}`); | ||
return key; | ||
} | ||
// Takes 64 bytes | ||
function getKeyFromHash(hashed) { | ||
// First 32 bytes of 64b uniformingly random input are taken, | ||
// clears 3 bits of it to produce a random field element. | ||
const keyb = typeof key === 'bigint' || typeof key === 'number' | ||
? ut.numberToBytesLE(normalizeScalar(key, maxGroupElement), groupLen) | ||
: key; | ||
// Hash private key with curve's hash function to produce uniformingly random input | ||
// We check byte lengths e.g.: ensureBytes(64, hash(ensureBytes(32, key))) | ||
const hashed = ensureBytes(CURVE.hash(ensureBytes(keyb, groupLen)), 2 * groupLen); | ||
// First half's bits are cleared to produce a random field element. | ||
const head = adjustScalarBytes(hashed.slice(0, groupLen)); | ||
// Second 32 bytes is called key prefix (5.1.6) | ||
// Second half is called key prefix (5.1.6) | ||
const prefix = hashed.slice(groupLen, 2 * groupLen); | ||
// The actual private scalar | ||
const scalar = modlLE(head); | ||
const scalar = modnLE(head); | ||
// Point on Edwards curve aka public key | ||
const point = Point.BASE.multiply(scalar); | ||
// Uint8Array representation | ||
const pointBytes = point.toRawBytes(); | ||
return { head, prefix, scalar, point, pointBytes }; | ||
} | ||
/** Convenience method that creates public key and other stuff. RFC8032 5.1.5 */ | ||
function getExtendedPublicKey(key) { | ||
return getKeyFromHash(CURVE.hash(checkPrivateKey(key))); | ||
} | ||
/** | ||
@@ -518,3 +503,3 @@ * Calculates ed25519 public key. RFC8032 5.1.5 | ||
context = ensureBytes(context); | ||
return modlLE(CURVE.hash(domain(message, context, !!CURVE.preHash))); | ||
return modnLE(CURVE.hash(domain(message, context, !!CURVE.preHash))); | ||
} | ||
@@ -527,5 +512,5 @@ /** Signs message with privateKey. RFC8032 5.1.6 */ | ||
const { prefix, scalar, pointBytes } = getExtendedPublicKey(privateKey); | ||
const r = hashDomainToScalar(concatBytes(prefix, message), context); | ||
const r = hashDomainToScalar(ut.concatBytes(prefix, message), context); | ||
const R = Point.BASE.multiply(r); // R = rG | ||
const k = hashDomainToScalar(concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg) | ||
const k = hashDomainToScalar(ut.concatBytes(R.toRawBytes(), pointBytes, message), context); // k = hash(R+P+msg) | ||
const s = mod.mod(r + k * scalar, CURVE_ORDER); // s = r + kp | ||
@@ -569,7 +554,7 @@ return new Signature(R, s).toRawBytes(); | ||
const SB = ExtendedPoint.BASE.multiplyUnsafe(s); | ||
const k = hashDomainToScalar(concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context); | ||
const k = hashDomainToScalar(ut.concatBytes(r.toRawBytes(), publicKey.toRawBytes(), message), context); | ||
const kA = ExtendedPoint.fromAffine(publicKey).multiplyUnsafe(k); | ||
const RkA = ExtendedPoint.fromAffine(r).add(kA); | ||
// [8][S]B = [8]R + [8][k]A' | ||
return RkA.subtract(SB).multiplyUnsafe(CURVE.h).equals(ExtendedPoint.ZERO); | ||
return RkA.subtract(SB).clearCofactor().equals(ExtendedPoint.ZERO); | ||
} | ||
@@ -580,8 +565,6 @@ // Enable precomputes. Slows down first publicKey computation by 20ms. | ||
getExtendedPublicKey, | ||
mod: modP, | ||
invert: Fp.invert, | ||
/** | ||
* Not needed for ed25519 private keys. Needed if you use scalars directly (rare). | ||
*/ | ||
hashToPrivateScalar: (hash) => hashToPrivateScalar(hash, CURVE_ORDER, true), | ||
hashToPrivateScalar: (hash) => ut.hashToPrivateScalar(hash, CURVE_ORDER, true), | ||
/** | ||
@@ -591,3 +574,3 @@ * ed25519 private keys are uniform 32-bit strings. We do not need to check for | ||
*/ | ||
randomPrivateKey: () => randomBytes(fieldLen), | ||
randomPrivateKey: () => randomBytes(Fp.BYTES), | ||
/** | ||
@@ -594,0 +577,0 @@ * We're doing scalar multiplication (used in getPublicKey etc) with precomputed BASE_POINT |
@@ -58,2 +58,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
while (a !== _0n) { | ||
// JIT applies optimization if those two lines follow each other | ||
const q = b / a; | ||
@@ -72,3 +73,4 @@ const r = b % a; | ||
// Tonelli-Shanks algorithm | ||
// https://eprint.iacr.org/2012/685.pdf (page 12) | ||
// Paper 1: https://eprint.iacr.org/2012/685.pdf (page 12) | ||
// Paper 2: Square Roots from 1; 24, 51, 10 to Dan Shanks | ||
export function tonelliShanks(P) { | ||
@@ -83,3 +85,3 @@ // Legendre constant: used to calculate Legendre symbol (a | p), | ||
// Step 1: By factoring out powers of 2 from p - 1, | ||
// find q and s such that p - 1 = q2s with q odd | ||
// find q and s such that p - 1 = q*(2^s) with q odd | ||
for (Q = P - _1n, S = 0; Q % _2n === _0n; Q /= _2n, S++) | ||
@@ -103,27 +105,28 @@ ; | ||
return function tonelliSlow(Fp, n) { | ||
// Step 0: Check that n is indeed a square: (n | p) must be ≡ 1 | ||
if (Fp.pow(n, legendreC) !== Fp.ONE) | ||
// Step 0: Check that n is indeed a square: (n | p) should not be ≡ -1 | ||
if (Fp.pow(n, legendreC) === Fp.negate(Fp.ONE)) | ||
throw new Error('Cannot find square root'); | ||
let s = S; | ||
let c = pow(Z, Q, P); | ||
let r = Fp.pow(n, Q1div2); | ||
let t = Fp.pow(n, Q); | ||
let t2 = Fp.ZERO; | ||
while (!Fp.equals(Fp.sub(t, Fp.ONE), Fp.ZERO)) { | ||
t2 = Fp.square(t); | ||
let i; | ||
for (i = 1; i < s; i++) { | ||
// stop if t2-1 == 0 | ||
if (Fp.equals(Fp.sub(t2, Fp.ONE), Fp.ZERO)) | ||
let r = S; | ||
// TODO: will fail at Fp2/etc | ||
let g = Fp.pow(Fp.mul(Fp.ONE, Z), Q); // will update both x and b | ||
let x = Fp.pow(n, Q1div2); // first guess at the square root | ||
let b = Fp.pow(n, Q); // first guess at the fudge factor | ||
while (!Fp.equals(b, Fp.ONE)) { | ||
if (Fp.equals(b, Fp.ZERO)) | ||
return Fp.ZERO; // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm (4. If t = 0, return r = 0) | ||
// Find m such b^(2^m)==1 | ||
let m = 1; | ||
for (let t2 = Fp.square(b); m < r; m++) { | ||
if (Fp.equals(t2, Fp.ONE)) | ||
break; | ||
// t2 *= t2 | ||
t2 = Fp.square(t2); | ||
t2 = Fp.square(t2); // t2 *= t2 | ||
} | ||
let b = pow(c, BigInt(1 << (s - i - 1)), P); | ||
r = Fp.mul(r, b); | ||
c = mod(b * b, P); | ||
t = Fp.mul(t, c); | ||
s = i; | ||
// NOTE: r-m-1 can be bigger than 32, need to convert to bigint before shift, otherwise there will be overflow | ||
const ge = Fp.pow(g, _1n << BigInt(r - m - 1)); // ge = 2^(r-m-1) | ||
g = Fp.square(ge); // g = ge * ge | ||
x = Fp.mul(x, ge); // x *= ge | ||
b = Fp.mul(b, g); // b *= g | ||
r = m; | ||
} | ||
return r; | ||
return x; | ||
}; | ||
@@ -130,0 +133,0 @@ } |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import * as mod from './modular.js'; | ||
import { ensureBytes, numberToBytesLE, bytesToNumberLE, | ||
// nLength, | ||
} from './utils.js'; | ||
import { ensureBytes, numberToBytesLE, bytesToNumberLE, isPositiveInt } from './utils.js'; | ||
const _0n = BigInt(0); | ||
@@ -16,3 +14,3 @@ const _1n = BigInt(1); | ||
continue; // Optional | ||
if (!Number.isSafeInteger(curve[i])) | ||
if (!isPositiveInt(curve[i])) | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
@@ -19,0 +17,0 @@ } |
@@ -6,7 +6,12 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
const _2n = BigInt(2); | ||
// Bans floats and integers above 2^53-1 | ||
export function isPositiveInt(num) { | ||
return typeof num === 'number' && Number.isSafeInteger(num) && num > 0; | ||
} | ||
export function validateOpts(curve) { | ||
mod.validateField(curve.Fp); | ||
for (const i of ['n', 'h']) { | ||
if (typeof curve[i] !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
const val = curve[i]; | ||
if (typeof val !== 'bigint') | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
@@ -18,6 +23,7 @@ if (!curve.Fp.isValid(curve.Gx)) | ||
for (const i of ['nBitLength', 'nByteLength']) { | ||
if (curve[i] === undefined) | ||
const val = curve[i]; | ||
if (val === undefined) | ||
continue; // Optional | ||
if (!Number.isSafeInteger(curve[i])) | ||
throw new Error(`Invalid curve param ${i}=${curve[i]} (${typeof curve[i]})`); | ||
if (!isPositiveInt(val)) | ||
throw new Error(`Invalid curve param ${i}=${val} (${typeof val})`); | ||
} | ||
@@ -109,17 +115,18 @@ // Set defaults | ||
/** | ||
* FIPS 186 B.4.1-compliant "constant-time" private key generation utility. | ||
* Can take (n+8) or more bytes of uniform input e.g. from CSPRNG or KDF | ||
* and convert them into private scalar, with the modulo bias being neglible. | ||
* As per FIPS 186 B.4.1. | ||
* Needs at least 40 bytes of input for 32-byte private key. | ||
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/ | ||
* @param hash hash output from sha512, or a similar function | ||
* @param hash hash output from SHA3 or a similar function | ||
* @returns valid private scalar | ||
*/ | ||
export function hashToPrivateScalar(hash, CURVE_ORDER, isLE = false) { | ||
export function hashToPrivateScalar(hash, groupOrder, isLE = false) { | ||
hash = ensureBytes(hash); | ||
const orderLen = nLength(CURVE_ORDER).nByteLength; | ||
const minLen = orderLen + 8; | ||
if (orderLen < 16 || hash.length < minLen || hash.length > 1024) | ||
throw new Error('Expected valid bytes of private key as per FIPS 186'); | ||
const hashLen = hash.length; | ||
const minLen = nLength(groupOrder).nByteLength + 8; | ||
if (minLen < 24 || hashLen < minLen || hashLen > 1024) | ||
throw new Error(`hashToPrivateScalar: expected ${minLen}-1024 bytes of input, got ${hashLen}`); | ||
const num = isLE ? bytesToNumberLE(hash) : bytesToNumberBE(hash); | ||
return mod.mod(num, CURVE_ORDER - _1n) + _1n; | ||
return mod.mod(num, groupOrder - _1n) + _1n; | ||
} | ||
@@ -126,0 +133,0 @@ export function equalBytes(b1, b2) { |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// Short Weierstrass curve. The formula is: y² = x³ + ax + b | ||
// TODO: sync vs async naming | ||
// TODO: default randomBytes | ||
// Differences from @noble/secp256k1 1.7: | ||
@@ -10,8 +8,9 @@ // 1. Different double() formula (but same addition) | ||
// 4. DRBG supports outputLen bigger than outputLen of hmac | ||
// 5. Support for different hash functions | ||
import * as mod from './modular.js'; | ||
import { bytesToHex, bytesToNumberBE, concatBytes, ensureBytes, hexToBytes, hexToNumber, numberToHexUnpadded, hashToPrivateScalar, } from './utils.js'; | ||
import * as utils from './utils.js'; | ||
import * as ut from './utils.js'; | ||
import { bytesToHex } from './utils.js'; | ||
import { hash_to_field, validateHTFOpts } from './hash-to-curve.js'; | ||
import { wNAF } from './group.js'; | ||
// DER encoding utilities | ||
// ASN.1 DER encoding utilities | ||
class DERError extends Error { | ||
@@ -22,42 +21,40 @@ constructor(message) { | ||
} | ||
function sliceDER(s) { | ||
// Proof: any([(i>=0x80) == (int(hex(i).replace('0x', '').zfill(2)[0], 16)>=8) for i in range(0, 256)]) | ||
// Padding done by numberToHex | ||
return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s; | ||
} | ||
function parseDERInt(data) { | ||
if (data.length < 2 || data[0] !== 0x02) { | ||
throw new DERError(`Invalid signature integer tag: ${bytesToHex(data)}`); | ||
} | ||
const len = data[1]; | ||
const res = data.subarray(2, len + 2); | ||
if (!len || res.length !== len) { | ||
throw new DERError(`Invalid signature integer: wrong length`); | ||
} | ||
// Strange condition, its not about length, but about first bytes of number. | ||
if (res[0] === 0x00 && res[1] <= 0x7f) { | ||
throw new DERError('Invalid signature integer: trailing length'); | ||
} | ||
return { data: bytesToNumberBE(res), left: data.subarray(len + 2) }; | ||
} | ||
function parseDERSignature(data) { | ||
if (data.length < 2 || data[0] != 0x30) { | ||
throw new DERError(`Invalid signature tag: ${bytesToHex(data)}`); | ||
} | ||
if (data[1] !== data.length - 2) { | ||
throw new DERError('Invalid signature: incorrect length'); | ||
} | ||
const { data: r, left: sBytes } = parseDERInt(data.subarray(2)); | ||
const { data: s, left: rBytesLeft } = parseDERInt(sBytes); | ||
if (rBytesLeft.length) { | ||
throw new DERError(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`); | ||
} | ||
return { r, s }; | ||
} | ||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n | ||
const _0n = BigInt(0); | ||
const _1n = BigInt(1); | ||
const _3n = BigInt(3); | ||
const DER = { | ||
slice(s) { | ||
// Proof: any([(i>=0x80) == (int(hex(i).replace('0x', '').zfill(2)[0], 16)>=8) for i in range(0, 256)]) | ||
// Padding done by numberToHex | ||
return Number.parseInt(s[0], 16) >= 8 ? '00' + s : s; | ||
}, | ||
parseInt(data) { | ||
if (data.length < 2 || data[0] !== 0x02) { | ||
throw new DERError(`Invalid signature integer tag: ${bytesToHex(data)}`); | ||
} | ||
const len = data[1]; | ||
const res = data.subarray(2, len + 2); | ||
if (!len || res.length !== len) { | ||
throw new DERError(`Invalid signature integer: wrong length`); | ||
} | ||
// Strange condition, its not about length, but about first bytes of number. | ||
if (res[0] === 0x00 && res[1] <= 0x7f) { | ||
throw new DERError('Invalid signature integer: trailing length'); | ||
} | ||
return { data: ut.bytesToNumberBE(res), left: data.subarray(len + 2) }; | ||
}, | ||
parseSig(data) { | ||
if (data.length < 2 || data[0] != 0x30) { | ||
throw new DERError(`Invalid signature tag: ${bytesToHex(data)}`); | ||
} | ||
if (data[1] !== data.length - 2) { | ||
throw new DERError('Invalid signature: incorrect length'); | ||
} | ||
const { data: r, left: sBytes } = DER.parseInt(data.subarray(2)); | ||
const { data: s, left: rBytesLeft } = DER.parseInt(sBytes); | ||
if (rBytesLeft.length) { | ||
throw new DERError(`Invalid signature: left bytes after parsing: ${bytesToHex(rBytesLeft)}`); | ||
} | ||
return { r, s }; | ||
}, | ||
}; | ||
function validatePointOpts(curve) { | ||
const opts = utils.validateOpts(curve); | ||
const opts = ut.validateOpts(curve); | ||
const Fp = opts.Fp; | ||
@@ -95,17 +92,9 @@ for (const i of ['a', 'b']) { | ||
} | ||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n | ||
const _0n = BigInt(0); | ||
const _1n = BigInt(1); | ||
const _3n = BigInt(3); | ||
export function weierstrassPoints(opts) { | ||
const CURVE = validatePointOpts(opts); | ||
const Fp = CURVE.Fp; | ||
// Lengths | ||
// All curves has same field / group length as for now, but it can be different for other curves | ||
const { nByteLength, nBitLength } = CURVE; | ||
const groupLen = nByteLength; | ||
// Not using ** operator with bigints for old engines. | ||
// 2n ** (8n * 32n) == 2n << (8n * 32n - 1n) | ||
//const FIELD_MASK = _2n << (_8n * BigInt(fieldLen) - _1n); | ||
// function numToFieldStr(num: bigint): string { | ||
// if (typeof num !== 'bigint') throw new Error('Expected bigint'); | ||
// if (!(_0n <= num && num < FIELD_MASK)) throw new Error(`Expected number < 2^${fieldLen * 8}`); | ||
// return num.toString(16).padStart(2 * fieldLen, '0'); | ||
// } | ||
const { Fp } = CURVE; // All curves has same field / group length as for now, but they can differ | ||
/** | ||
@@ -121,14 +110,22 @@ * y² = x³ + ax + b: Short weierstrass curve formula | ||
} | ||
// Valid group elements reside in range 1..n-1 | ||
function isWithinCurveOrder(num) { | ||
return _0n < num && num < CURVE.n; | ||
} | ||
/** | ||
* 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` | ||
*/ | ||
function normalizePrivateKey(key) { | ||
if (typeof CURVE.normalizePrivateKey === 'function') { | ||
key = CURVE.normalizePrivateKey(key); | ||
} | ||
const { normalizePrivateKey: custom, nByteLength: groupLen, wrapPrivateKey, n: order } = CURVE; | ||
if (typeof custom === 'function') | ||
key = custom(key); | ||
let num; | ||
if (typeof key === 'bigint') { | ||
// Curve order check is done below | ||
num = key; | ||
} | ||
else if (typeof key === 'number' && Number.isSafeInteger(key) && key > 0) { | ||
else if (ut.isPositiveInt(key)) { | ||
num = BigInt(key); | ||
@@ -139,3 +136,4 @@ } | ||
throw new Error(`Expected ${groupLen} bytes of private key`); | ||
num = hexToNumber(key); | ||
// Validates individual octets | ||
num = ut.hexToNumber(key); | ||
} | ||
@@ -145,3 +143,3 @@ else if (key instanceof Uint8Array) { | ||
throw new Error(`Expected ${groupLen} bytes of private key`); | ||
num = bytesToNumberBE(key); | ||
num = ut.bytesToNumberBE(key); | ||
} | ||
@@ -151,4 +149,5 @@ else { | ||
} | ||
if (CURVE.wrapPrivateKey) | ||
num = mod.mod(num, CURVE.n); | ||
// Useful for curves with cofactor != 1 | ||
if (wrapPrivateKey) | ||
num = mod.mod(num, order); | ||
if (!isWithinCurveOrder(num)) | ||
@@ -158,4 +157,8 @@ throw new Error('Expected private key: 0 < key < n'); | ||
} | ||
/** | ||
* Validates if a scalar ("private number") is valid. | ||
* Scalars are valid only if they are less than curve order. | ||
*/ | ||
function normalizeScalar(num) { | ||
if (typeof num === 'number' && Number.isSafeInteger(num) && num > 0) | ||
if (ut.isPositiveInt(num)) | ||
return BigInt(num); | ||
@@ -188,3 +191,3 @@ if (typeof num === 'bigint' && isWithinCurveOrder(num)) | ||
* Takes a bunch of Projective Points but executes only one | ||
* invert on all of them. invert is very slow operation, | ||
* inversion on all of them. Inversion is very slow operation, | ||
* so this improves performance massively. | ||
@@ -196,2 +199,5 @@ */ | ||
} | ||
/** | ||
* Optimization: converts a list of projective points to a list of identical points with Z=1. | ||
*/ | ||
static normalizeZ(points) { | ||
@@ -217,5 +223,2 @@ return ProjectivePoint.toAffineBatch(points).map(ProjectivePoint.fromAffine); | ||
} | ||
doubleAdd() { | ||
return this.add(this); | ||
} | ||
// Renes-Costello-Batina exception-free doubling formula. | ||
@@ -425,16 +428,15 @@ // There is 30% faster Jacobian formula, but it is not complete. | ||
isTorsionFree() { | ||
if (CURVE.h === _1n) | ||
return true; // No subgroups, always torsion fee | ||
if (CURVE.isTorsionFree) | ||
return CURVE.isTorsionFree(ProjectivePoint, this); | ||
// is multiplyUnsafe(CURVE.n) is always ok, same as for edwards? | ||
throw new Error('Unsupported!'); | ||
const { h: cofactor, isTorsionFree } = CURVE; | ||
if (cofactor === _1n) | ||
return true; // No subgroups, always torsion-free | ||
if (isTorsionFree) | ||
return isTorsionFree(ProjectivePoint, this); | ||
throw new Error('isTorsionFree() has not been declared for the elliptic curve'); | ||
} | ||
// Clear cofactor of G1 | ||
// https://eprint.iacr.org/2019/403 | ||
clearCofactor() { | ||
if (CURVE.h === _1n) | ||
const { h: cofactor, clearCofactor } = CURVE; | ||
if (cofactor === _1n) | ||
return this; // Fast-path | ||
if (CURVE.clearCofactor) | ||
return CURVE.clearCofactor(ProjectivePoint, this); | ||
if (clearCofactor) | ||
return clearCofactor(ProjectivePoint, this); | ||
return this.multiplyUnsafe(CURVE.h); | ||
@@ -445,3 +447,4 @@ } | ||
ProjectivePoint.ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO); | ||
const wnaf = wNAF(ProjectivePoint, CURVE.endo ? nBitLength / 2 : nBitLength); | ||
const _bits = CURVE.nBitLength; | ||
const wnaf = wNAF(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits); | ||
function assertPrjPoint(other) { | ||
@@ -477,3 +480,3 @@ if (!(other instanceof ProjectivePoint)) | ||
static fromHex(hex) { | ||
const { x, y } = CURVE.fromBytes(ensureBytes(hex)); | ||
const { x, y } = CURVE.fromBytes(ut.ensureBytes(hex)); | ||
const point = new Point(x, y); | ||
@@ -500,3 +503,3 @@ point.assertValidity(); | ||
return; | ||
throw new Error('Point is infinity'); | ||
throw new Error('Point at infinity'); | ||
} | ||
@@ -506,9 +509,9 @@ // Some 3rd-party test vectors require different wording between here & `fromCompressedHex` | ||
const { x, y } = this; | ||
// Check if x, y are valid field elements | ||
if (!Fp.isValid(x) || !Fp.isValid(y)) | ||
throw new Error(msg); | ||
const left = Fp.square(y); | ||
const right = weierstrassEquation(x); | ||
const left = Fp.square(y); // y² | ||
const right = weierstrassEquation(x); // x³ + ax + b | ||
if (!Fp.equals(left, right)) | ||
throw new Error(msg); | ||
// TODO: flag to disable this? | ||
if (!this.isTorsionFree()) | ||
@@ -526,11 +529,12 @@ throw new Error('Point must be of prime-order subgroup'); | ||
} | ||
toProj() { | ||
return ProjectivePoint.fromAffine(this); | ||
} | ||
// Adds point to itself | ||
double() { | ||
return ProjectivePoint.fromAffine(this).double().toAffine(); | ||
return this.toProj().double().toAffine(); | ||
} | ||
// Adds point to other point | ||
add(other) { | ||
return ProjectivePoint.fromAffine(this).add(ProjectivePoint.fromAffine(other)).toAffine(); | ||
return this.toProj().add(ProjectivePoint.fromAffine(other)).toAffine(); | ||
} | ||
// Subtracts other point from the point | ||
subtract(other) { | ||
@@ -540,12 +544,12 @@ return this.add(other.negate()); | ||
multiply(scalar) { | ||
return ProjectivePoint.fromAffine(this).multiply(scalar, this).toAffine(); | ||
return this.toProj().multiply(scalar, this).toAffine(); | ||
} | ||
multiplyUnsafe(scalar) { | ||
return ProjectivePoint.fromAffine(this).multiplyUnsafe(scalar).toAffine(); | ||
return this.toProj().multiplyUnsafe(scalar).toAffine(); | ||
} | ||
clearCofactor() { | ||
return ProjectivePoint.fromAffine(this).clearCofactor().toAffine(); | ||
return this.toProj().clearCofactor().toAffine(); | ||
} | ||
isTorsionFree() { | ||
return ProjectivePoint.fromAffine(this).isTorsionFree(); | ||
return this.toProj().isTorsionFree(); | ||
} | ||
@@ -559,3 +563,3 @@ /** | ||
multiplyAndAddUnsafe(Q, a, b) { | ||
const P = ProjectivePoint.fromAffine(this); | ||
const P = this.toProj(); | ||
const aP = a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a); | ||
@@ -569,18 +573,19 @@ const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b); | ||
static hashToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = ensureBytes(msg); | ||
const { mapToCurve } = CURVE; | ||
if (!mapToCurve) | ||
throw new Error('CURVE.mapToCurve() has not been defined'); | ||
msg = ut.ensureBytes(msg); | ||
const u = hash_to_field(msg, 2, { ...CURVE.htfDefaults, ...options }); | ||
const { x: x0, y: y0 } = CURVE.mapToCurve(u[0]); | ||
const { x: x1, y: y1 } = CURVE.mapToCurve(u[1]); | ||
const p = new Point(x0, y0).add(new Point(x1, y1)).clearCofactor(); | ||
return p; | ||
const { x: x0, y: y0 } = mapToCurve(u[0]); | ||
const { x: x1, y: y1 } = mapToCurve(u[1]); | ||
return new Point(x0, y0).add(new Point(x1, y1)).clearCofactor(); | ||
} | ||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-16#section-3 | ||
static encodeToCurve(msg, options) { | ||
if (!CURVE.mapToCurve) | ||
throw new Error('No mapToCurve defined for curve'); | ||
msg = ensureBytes(msg); | ||
const { mapToCurve } = CURVE; | ||
if (!mapToCurve) | ||
throw new Error('CURVE.mapToCurve() has not been defined'); | ||
msg = ut.ensureBytes(msg); | ||
const u = hash_to_field(msg, 1, { ...CURVE.htfDefaults, ...options }); | ||
const { x, y } = CURVE.mapToCurve(u[0]); | ||
const { x, y } = mapToCurve(u[0]); | ||
return new Point(x, y).clearCofactor(); | ||
@@ -590,7 +595,7 @@ } | ||
/** | ||
* Base point aka generator. public_key = Point.BASE * private_key | ||
* Base point aka generator. Any public_key = Point.BASE * private_key | ||
*/ | ||
Point.BASE = new Point(CURVE.Gx, CURVE.Gy); | ||
/** | ||
* Identity point aka point at infinity. point = point + zero_point | ||
* Identity point aka point at infinity. p - p = zero_p; p + zero_p = p | ||
*/ | ||
@@ -607,4 +612,4 @@ Point.ZERO = new Point(Fp.ZERO, Fp.ZERO); | ||
function validateOpts(curve) { | ||
const opts = utils.validateOpts(curve); | ||
if (typeof opts.hash !== 'function' || !Number.isSafeInteger(opts.hash.outputLen)) | ||
const opts = ut.validateOpts(curve); | ||
if (typeof opts.hash !== 'function' || !ut.isPositiveInt(opts.hash.outputLen)) | ||
throw new Error('Invalid hash function'); | ||
@@ -665,3 +670,3 @@ if (typeof opts.hmac !== 'function') | ||
} | ||
return concatBytes(...out); | ||
return ut.concatBytes(...out); | ||
} | ||
@@ -673,4 +678,4 @@ } | ||
const Fp = CURVE.Fp; | ||
const compressedLen = Fp.BYTES + 1; // 33 | ||
const uncompressedLen = 2 * Fp.BYTES + 1; // 65 | ||
const compressedLen = Fp.BYTES + 1; // e.g. 33 for 32 | ||
const uncompressedLen = 2 * Fp.BYTES + 1; // e.g. 65 for 32 | ||
function isValidFieldElement(num) { | ||
@@ -683,7 +688,9 @@ // 0 is disallowed by arbitrary reasons. Probably because infinity point? | ||
toBytes(c, point, isCompressed) { | ||
const x = Fp.toBytes(point.x); | ||
const cat = ut.concatBytes; | ||
if (isCompressed) { | ||
return concatBytes(new Uint8Array([point.hasEvenY() ? 0x02 : 0x03]), Fp.toBytes(point.x)); | ||
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x); | ||
} | ||
else { | ||
return concatBytes(new Uint8Array([0x04]), Fp.toBytes(point.x), Fp.toBytes(point.y)); | ||
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(point.y)); | ||
} | ||
@@ -696,3 +703,3 @@ }, | ||
if (len === compressedLen && (header === 0x02 || header === 0x03)) { | ||
const x = bytesToNumberBE(bytes.subarray(1)); | ||
const x = ut.bytesToNumberBE(bytes.subarray(1)); | ||
if (!isValidFieldElement(x)) | ||
@@ -750,13 +757,14 @@ throw new Error('Point is not on curve'); | ||
} | ||
function bits2int_2(bytes) { | ||
const delta = bytes.length * 8 - CURVE.nBitLength; | ||
const num = ut.bytesToNumberBE(bytes); | ||
return delta > 0 ? num >> BigInt(delta) : num; | ||
} | ||
// Ensures ECDSA message hashes are 32 bytes and < curve order | ||
function _truncateHash(hash, truncateOnly = false) { | ||
const { n, nBitLength } = CURVE; | ||
const byteLength = hash.length; | ||
const delta = byteLength * 8 - nBitLength; // size of curve.n (252 bits) | ||
let h = bytesToNumberBE(hash); | ||
if (delta > 0) | ||
h = h >> BigInt(delta); | ||
if (!truncateOnly && h >= n) | ||
h -= n; | ||
return h; | ||
const h = bits2int_2(hash); | ||
if (truncateOnly) | ||
return h; | ||
const { n } = CURVE; | ||
return h >= n ? h - n : h; | ||
} | ||
@@ -774,3 +782,3 @@ const truncateHash = CURVE.truncateHash || _truncateHash; | ||
} | ||
// pair (32 bytes of r, 32 bytes of s) | ||
// pair (bytes of r, bytes of s) | ||
static fromCompact(hex) { | ||
@@ -782,5 +790,7 @@ const arr = hex instanceof Uint8Array; | ||
const str = arr ? bytesToHex(hex) : hex; | ||
if (str.length !== 128) | ||
throw new Error(`${name}: Expected 64-byte hex`); | ||
return new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128))); | ||
const gl = CURVE.nByteLength * 2; // group length in hex, not ui8a | ||
if (str.length !== 2 * gl) | ||
throw new Error(`${name}: Expected ${gl / 2}-byte hex`); | ||
const slice = (from, to) => ut.hexToNumber(str.slice(from, to)); | ||
return new Signature(slice(0, gl), slice(gl, 2 * gl)); | ||
} | ||
@@ -793,3 +803,3 @@ // DER encoded ECDSA signature | ||
throw new TypeError(`Signature.fromDER: Expected string or Uint8Array`); | ||
const { r, s } = parseDERSignature(arr ? hex : hexToBytes(hex)); | ||
const { r, s } = DER.parseSig(arr ? hex : ut.hexToBytes(hex)); | ||
return new Signature(r, s); | ||
@@ -810,2 +820,3 @@ } | ||
* https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Public_key_recovery | ||
* It's also possible to recover key without bit: try all 4 bit values and check for sig match. | ||
* | ||
@@ -826,7 +837,10 @@ * ``` | ||
throw new Error('Cannot recover: recovery bit is not present'); | ||
if (recovery !== 0 && recovery !== 1) | ||
if (![0, 1, 2, 3].includes(recovery)) | ||
throw new Error('Cannot recover: invalid recovery bit'); | ||
const h = truncateHash(ensureBytes(msgHash)); | ||
const h = truncateHash(ut.ensureBytes(msgHash)); | ||
const { n } = CURVE; | ||
const rinv = mod.invert(r, n); | ||
const radj = recovery === 2 || recovery === 3 ? r + n : r; | ||
if (radj >= Fp.ORDER) | ||
throw new Error('Cannot recover: bit 2/3 is invalid with current r'); | ||
const rinv = mod.invert(radj, n); | ||
// Q = u1⋅G + u2⋅R | ||
@@ -836,4 +850,4 @@ const u1 = mod.mod(-h * rinv, n); | ||
const prefix = recovery & 1 ? '03' : '02'; | ||
const R = Point.fromHex(prefix + numToFieldStr(r)); | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); | ||
const R = Point.fromHex(prefix + numToFieldStr(radj)); | ||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // unsafe is fine: no priv data leaked | ||
if (!Q) | ||
@@ -858,18 +872,19 @@ throw new Error('Cannot recover: point at infinify'); | ||
// DER-encoded | ||
toDERRawBytes(isCompressed = false) { | ||
return hexToBytes(this.toDERHex(isCompressed)); | ||
toDERRawBytes() { | ||
return ut.hexToBytes(this.toDERHex()); | ||
} | ||
toDERHex(isCompressed = false) { | ||
const sHex = sliceDER(numberToHexUnpadded(this.s)); | ||
if (isCompressed) | ||
return sHex; | ||
const rHex = sliceDER(numberToHexUnpadded(this.r)); | ||
const rLen = numberToHexUnpadded(rHex.length / 2); | ||
const sLen = numberToHexUnpadded(sHex.length / 2); | ||
const length = numberToHexUnpadded(rHex.length / 2 + sHex.length / 2 + 4); | ||
toDERHex() { | ||
const { numberToHexUnpadded: toHex } = ut; | ||
const sHex = DER.slice(toHex(this.s)); | ||
const rHex = DER.slice(toHex(this.r)); | ||
const sHexL = sHex.length / 2; | ||
const rHexL = rHex.length / 2; | ||
const sLen = toHex(sHexL); | ||
const rLen = toHex(rHexL); | ||
const length = toHex(rHexL + sHexL + 4); | ||
return `30${length}02${rLen}${rHex}02${sLen}${sHex}`; | ||
} | ||
// 32 bytes of r, then 32 bytes of s | ||
// padded bytes of r, then padded bytes of s | ||
toCompactRawBytes() { | ||
return hexToBytes(this.toCompactHex()); | ||
return ut.hexToBytes(this.toCompactHex()); | ||
} | ||
@@ -881,4 +896,2 @@ toCompactHex() { | ||
const utils = { | ||
mod: (n, modulo = Fp.ORDER) => mod.mod(n, modulo), | ||
invert: Fp.invert, | ||
isValidPrivateKey(privateKey) { | ||
@@ -903,3 +916,3 @@ try { | ||
*/ | ||
hashToPrivateKey: (hash) => numToField(hashToPrivateScalar(hash, CURVE_ORDER)), | ||
hashToPrivateKey: (hash) => numToField(ut.hashToPrivateScalar(hash, CURVE_ORDER)), | ||
/** | ||
@@ -926,6 +939,6 @@ * Produces cryptographically secure private key from random of size (nBitLength+64) | ||
/** | ||
* Computes public key for a private key. | ||
* Computes public key for a private key. Checks for validity of the private key. | ||
* @param privateKey private key | ||
* @param isCompressed whether to return compact, or full key | ||
* @returns Public key, full by default; short when isCompressed=true | ||
* @param isCompressed whether to return compact (default), or full key | ||
* @returns Public key, full when isCompressed=false; short when isCompressed=true | ||
*/ | ||
@@ -951,8 +964,8 @@ function getPublicKey(privateKey, isCompressed = false) { | ||
/** | ||
* ECDH (Elliptic Curve Diffie Hellman) implementation. | ||
* 1. Checks for validity of private key | ||
* 2. Checks for the public key of being on-curve | ||
* ECDH (Elliptic Curve Diffie Hellman). | ||
* Computes shared public key from private key and public key. | ||
* Checks: 1) private key validity 2) shared key is on-curve | ||
* @param privateA private key | ||
* @param publicB different public key | ||
* @param isCompressed whether to return compact (33-byte), or full (65-byte) key | ||
* @param isCompressed whether to return compact (default), or full key | ||
* @returns shared public key | ||
@@ -971,4 +984,17 @@ */ | ||
function bits2int(bytes) { | ||
const slice = bytes.length > Fp.BYTES ? bytes.slice(0, Fp.BYTES) : bytes; | ||
return bytesToNumberBE(slice); | ||
const { nByteLength } = CURVE; | ||
if (!(bytes instanceof Uint8Array)) | ||
throw new Error('Expected Uint8Array'); | ||
const slice = bytes.length > nByteLength ? bytes.slice(0, nByteLength) : bytes; | ||
// const slice = bytes; nByteLength; nBitLength; | ||
let num = ut.bytesToNumberBE(slice); | ||
// const { nBitLength } = CURVE; | ||
// const delta = (bytes.length * 8) - nBitLength; | ||
// if (delta > 0) { | ||
// // console.log('bits=', bytes.length*8, 'CURVE n=', nBitLength, 'delta=', delta); | ||
// // console.log(bytes.length, nBitLength, delta); | ||
// // console.log(bytes, new Error().stack); | ||
// num >>= BigInt(delta); | ||
// } | ||
return num; | ||
} | ||
@@ -981,3 +1007,3 @@ function bits2octets(bytes) { | ||
function int2octets(num) { | ||
return numToField(num); // prohibits >32 bytes | ||
return numToField(num); // prohibits >nByteLength bytes | ||
} | ||
@@ -990,3 +1016,3 @@ // Steps A, D of RFC6979 3.2 | ||
// Step A is ignored, since we already provide hash instead of msg | ||
const h1 = numToField(truncateHash(ensureBytes(msgHash))); | ||
const h1 = numToField(truncateHash(ut.ensureBytes(msgHash))); | ||
const d = normalizePrivateKey(privateKey); | ||
@@ -999,3 +1025,3 @@ // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k') | ||
extraEntropy = CURVE.randomBytes(Fp.BYTES); | ||
const e = ensureBytes(extraEntropy); | ||
const e = ut.ensureBytes(extraEntropy); | ||
if (e.length !== Fp.BYTES) | ||
@@ -1008,3 +1034,3 @@ throw new Error(`sign: Expected ${Fp.BYTES} bytes of extra data`); | ||
// V, 0x00 are done in HmacDRBG constructor. | ||
const seed = concatBytes(...seedArgs); | ||
const seed = ut.concatBytes(...seedArgs); | ||
const m = bits2int(h1); | ||
@@ -1033,6 +1059,7 @@ return { seed, m, d }; | ||
return; | ||
// s = (1/k * (m + dr) mod n | ||
// s = (m + dr)/k mod n where x/k == x*inv(k) | ||
const s = mod.mod(kinv * mod.mod(m + mod.mod(d * r, n), n), n); | ||
if (s === _0n) | ||
return; | ||
// recovery bit is usually 0 or 1; rarely it's 2 or 3, when q.x > n | ||
let recovery = (q.x === r ? 0 : 2) | Number(q.y & _1n); | ||
@@ -1046,7 +1073,14 @@ let normS = s; | ||
} | ||
const defaultSigOpts = { lowS: CURVE.lowS }; | ||
/** | ||
* Signs message hash (not message: you need to hash it by yourself). | ||
* ``` | ||
* sign(m, d, k) where | ||
* (x, y) = G × k | ||
* r = x mod n | ||
* s = (m + dr)/k mod n | ||
* ``` | ||
* @param opts `lowS, extraEntropy` | ||
*/ | ||
function sign(msgHash, privKey, opts = { lowS: CURVE.lowS }) { | ||
function sign(msgHash, privKey, opts = defaultSigOpts) { | ||
// Steps A, D of RFC6979 3.2. | ||
@@ -1063,2 +1097,8 @@ const { seed, m, d } = initSigArgs(msgHash, privKey, opts.extraEntropy); | ||
} | ||
/** | ||
* Signs a message (not message hash). | ||
*/ | ||
function signUnhashed(msg, privKey, opts = defaultSigOpts) { | ||
return sign(CURVE.hash(ut.ensureBytes(msg)), privKey, opts); | ||
} | ||
// Enable precomputes. Slows down first publicKey computation by 20ms. | ||
@@ -1096,3 +1136,3 @@ Point.BASE._setWindowSize(8); | ||
} | ||
msgHash = ensureBytes(msgHash); | ||
msgHash = ut.ensureBytes(msgHash); | ||
} | ||
@@ -1131,2 +1171,3 @@ catch (error) { | ||
sign, | ||
signUnhashed, | ||
verify, | ||
@@ -1133,0 +1174,0 @@ Point, |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
// The pairing-friendly Barreto-Lynn-Scott elliptic curve construction allows to: | ||
// - Construct zk-SNARKs at the 128-bit security | ||
// - Use threshold signatures, which allows a user to sign lots of messages with one signature and verify them swiftly in a batch, using Boneh-Lynn-Shacham signature scheme. | ||
// Differences from @noble/bls12-381 1.4: | ||
// - PointG1 -> G1.Point | ||
// - PointG2 -> G2.Point | ||
// - PointG2.fromSignature -> Signature.decode | ||
// - PointG2.toSignature -> Signature.encode | ||
// - Fixed Fp2 ORDER | ||
// - Points now have only two coordinates | ||
import { sha256 } from '@noble/hashes/sha256'; | ||
@@ -10,9 +20,2 @@ import { randomBytes } from '@noble/hashes/utils'; | ||
import { isogenyMap } from './abstract/hash-to-curve.js'; | ||
// Differences from bls12-381: | ||
// - PointG1 -> G1.Point | ||
// - PointG2 -> G2.Point | ||
// - PointG2.fromSignature -> Signature.decode | ||
// - PointG2.toSignature -> Signature.encode | ||
// - Fixed Fp2 ORDER | ||
// Points now have only two coordinates | ||
// CURVE FIELDS | ||
@@ -101,2 +104,4 @@ // Finite field over p. | ||
sqrt: (num) => { | ||
if (Fp2.equals(num, Fp2.ZERO)) | ||
return Fp2.ZERO; // Algo doesn't handles this case | ||
// TODO: Optimize this line. It's extremely slow. | ||
@@ -103,0 +108,0 @@ // Speeding this up would boost aggregateSignatures. |
@@ -225,3 +225,3 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
const MAX_255B = BigInt('0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'); | ||
const bytes255ToNumberLE = (bytes) => ed25519.utils.mod(bytesToNumberLE(bytes) & MAX_255B); | ||
const bytes255ToNumberLE = (bytes) => ed25519.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B); | ||
// Computes Elligator map for Ristretto | ||
@@ -232,3 +232,3 @@ // https://ristretto.group/formulas/elligator.html | ||
const P = ed25519.CURVE.Fp.ORDER; | ||
const { mod } = ed25519.utils; | ||
const mod = ed25519.CURVE.Fp.create; | ||
const r = mod(SQRT_M1 * r0 * r0); // 1 | ||
@@ -291,3 +291,3 @@ const Ns = mod((r + _1n) * ONE_MINUS_D_SQ); // 2 | ||
const P = ed25519.CURVE.Fp.ORDER; | ||
const { mod } = ed25519.utils; | ||
const mod = ed25519.CURVE.Fp.create; | ||
const emsg = 'RistrettoPoint.fromHex: the hex is not valid encoding of RistrettoPoint'; | ||
@@ -324,3 +324,3 @@ const s = bytes255ToNumberLE(hex); | ||
const P = ed25519.CURVE.Fp.ORDER; | ||
const { mod } = ed25519.utils; | ||
const mod = ed25519.CURVE.Fp.create; | ||
const u1 = mod(mod(z + y) * mod(z - y)); // 1 | ||
@@ -363,3 +363,3 @@ const u2 = mod(x * y); // 2 | ||
const b = other.ep; | ||
const { mod } = ed25519.utils; | ||
const mod = ed25519.CURVE.Fp.create; | ||
// (x1 * y2 == y1 * x2) | (y1 * y2 == x1 * x2) | ||
@@ -366,0 +366,0 @@ const one = mod(a.x * b.y) === mod(a.y * b.x); |
@@ -129,3 +129,4 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
Fp, | ||
// Subgroup order: how many points ed448 has; 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n | ||
// Subgroup order: how many points curve has; | ||
// 2n**446n - 13818066809895115352007386748515426880336692474882178609894547503885n | ||
n: BigInt('181709681073901722637330951972001133588410340171829515070372549795146003961539585716195755291692375963310293709091662304773755859649779'), | ||
@@ -132,0 +133,0 @@ nBitLength: 456, |
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
import { sha256 } from '@noble/hashes/sha256'; | ||
import { sha512 } from '@noble/hashes/sha512'; | ||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils'; | ||
@@ -10,2 +10,3 @@ import { twistedEdwards } from './abstract/edwards.js'; | ||
* https://neuromancer.sk/std/other/JubJub | ||
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them. | ||
*/ | ||
@@ -17,5 +18,5 @@ export const jubjub = twistedEdwards({ | ||
// Finite field 𝔽p over which we'll do calculations | ||
// Same value as bls12-381 Fr (not Fp) | ||
Fp: Fp(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')), | ||
// Subgroup order: how many points ed25519 has | ||
// 2n ** 252n + 27742317777372353535851937790883648493n; | ||
// Subgroup order: how many points curve has | ||
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'), | ||
@@ -27,3 +28,3 @@ // Cofactor | ||
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'), | ||
hash: sha256, | ||
hash: sha512, | ||
randomBytes, | ||
@@ -30,0 +31,0 @@ }); |
@@ -10,6 +10,4 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
/** | ||
* secp256k1 belongs to Koblitz curves: it has | ||
* efficiently computable Frobenius endomorphism. | ||
* Endomorphism improves efficiency: | ||
* Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%. | ||
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism. | ||
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%. | ||
* Should always be used for Projective's double-and-add multiplication. | ||
@@ -115,3 +113,3 @@ * For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit. | ||
const b2 = a1; | ||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); | ||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (2n**128n).toString(16) | ||
const c1 = divNearest(b2 * k, n); | ||
@@ -160,18 +158,16 @@ const c2 = divNearest(-b1 * k, n); | ||
// Schnorr is 32 bytes | ||
if (bytes.length === 32) { | ||
const x = bytesToNumberBE(bytes); | ||
if (!isValidFieldElement(x)) | ||
throw new Error('Point is not on curve'); | ||
const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b | ||
let y = sqrtMod(y2); // y = y² ^ (p+1)/4 | ||
const isYOdd = (y & _1n) === _1n; | ||
// Schnorr | ||
if (isYOdd) | ||
y = secp256k1.CURVE.Fp.negate(y); | ||
const point = new secp256k1.Point(x, y); | ||
point.assertValidity(); | ||
return point; | ||
} | ||
// Do we need that in schnorr at all? | ||
return secp256k1.Point.fromHex(publicKey); | ||
if (bytes.length !== 32) | ||
throw new Error('Schnorr pubkeys must be 32 bytes'); | ||
const x = bytesToNumberBE(bytes); | ||
if (!isValidFieldElement(x)) | ||
throw new Error('Point is not on curve'); | ||
const y2 = secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b | ||
let y = sqrtMod(y2); // y = y² ^ (p+1)/4 | ||
const isYOdd = (y & _1n) === _1n; | ||
// Schnorr | ||
if (isYOdd) | ||
y = secp256k1.CURVE.Fp.negate(y); | ||
const point = new secp256k1.Point(x, y); | ||
point.assertValidity(); | ||
return point; | ||
} | ||
@@ -212,6 +208,7 @@ } | ||
const bytes = ensureBytes(hex); | ||
if (bytes.length !== 64) | ||
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`); | ||
const r = bytesToNumberBE(bytes.subarray(0, 32)); | ||
const s = bytesToNumberBE(bytes.subarray(32, 64)); | ||
const len = 32; // group length | ||
if (bytes.length !== 2 * len) | ||
throw new TypeError(`SchnorrSignature.fromHex: expected ${2 * len} bytes, not ${bytes.length}`); | ||
const r = bytesToNumberBE(bytes.subarray(0, len)); | ||
const s = bytesToNumberBE(bytes.subarray(len, 2 * len)); | ||
return new SchnorrSignature(r, s); | ||
@@ -218,0 +215,0 @@ } |
@@ -124,3 +124,4 @@ /*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
const sha256mask = 2n ** 256n; | ||
const limit = sha256mask - starkCurve.utils.mod(sha256mask, starkCurve.CURVE.n); | ||
const Fn = Fp(CURVE.n); | ||
const limit = sha256mask - Fn.create(sha256mask); | ||
for (let i = 0;; i++) { | ||
@@ -130,3 +131,3 @@ const key = hashKeyWithIndex(_seed, i); | ||
if (key < limit) | ||
return starkCurve.utils.mod(key, starkCurve.CURVE.n).toString(16); | ||
return Fn.create(key).toString(16); | ||
} | ||
@@ -133,0 +134,0 @@ } |
/** | ||
* jubjub Twisted Edwards curve. | ||
* https://neuromancer.sk/std/other/JubJub | ||
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them. | ||
*/ | ||
@@ -5,0 +6,0 @@ export declare const jubjub: import("./abstract/edwards.js").CurveFn; |
@@ -5,3 +5,3 @@ "use strict"; | ||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */ | ||
const sha256_1 = require("@noble/hashes/sha256"); | ||
const sha512_1 = require("@noble/hashes/sha512"); | ||
const utils_1 = require("@noble/hashes/utils"); | ||
@@ -14,2 +14,3 @@ const edwards_js_1 = require("./abstract/edwards.js"); | ||
* https://neuromancer.sk/std/other/JubJub | ||
* jubjub does not use EdDSA, so `hash`/sha512 params are passed because interface expects them. | ||
*/ | ||
@@ -21,5 +22,5 @@ exports.jubjub = (0, edwards_js_1.twistedEdwards)({ | ||
// Finite field 𝔽p over which we'll do calculations | ||
// Same value as bls12-381 Fr (not Fp) | ||
Fp: (0, modular_js_1.Fp)(BigInt('0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001')), | ||
// Subgroup order: how many points ed25519 has | ||
// 2n ** 252n + 27742317777372353535851937790883648493n; | ||
// Subgroup order: how many points curve has | ||
n: BigInt('0xe7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7'), | ||
@@ -31,3 +32,3 @@ // Cofactor | ||
Gy: BigInt('0x1d523cf1ddab1a1793132e78c866c0c33e26ba5cc220fed7cc3f870e59d292aa'), | ||
hash: sha256_1.sha256, | ||
hash: sha512_1.sha512, | ||
randomBytes: utils_1.randomBytes, | ||
@@ -34,0 +35,0 @@ }); |
@@ -41,6 +41,4 @@ export declare const P192: Readonly<{ | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
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; | ||
signUnhashed: (msg: Uint8Array, 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 | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -53,4 +51,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -108,6 +104,4 @@ _bigintToString: (num: bigint) => string; | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
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; | ||
signUnhashed: (msg: Uint8Array, 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 | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -120,4 +114,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -124,0 +116,0 @@ _bigintToString: (num: bigint) => string; |
@@ -41,6 +41,4 @@ export declare const P224: Readonly<{ | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
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; | ||
signUnhashed: (msg: Uint8Array, 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 | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -53,4 +51,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -108,6 +104,4 @@ _bigintToString: (num: bigint) => string; | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
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; | ||
signUnhashed: (msg: Uint8Array, 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 | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -120,4 +114,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -124,0 +116,0 @@ _bigintToString: (num: bigint) => string; |
@@ -41,6 +41,4 @@ export declare const P256: Readonly<{ | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
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; | ||
signUnhashed: (msg: Uint8Array, 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 | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -53,4 +51,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -108,6 +104,4 @@ _bigintToString: (num: bigint) => string; | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
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; | ||
signUnhashed: (msg: Uint8Array, 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 | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -120,4 +114,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -124,0 +116,0 @@ _bigintToString: (num: bigint) => string; |
@@ -41,6 +41,4 @@ export declare const P384: Readonly<{ | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
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; | ||
signUnhashed: (msg: Uint8Array, 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 | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -53,4 +51,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -108,6 +104,4 @@ _bigintToString: (num: bigint) => string; | ||
getSharedSecret: (privateA: import("./abstract/utils.js").PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: import("./abstract/utils.js").PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
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; | ||
signUnhashed: (msg: Uint8Array, 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 | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -120,4 +114,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -124,0 +116,0 @@ _bigintToString: (num: bigint) => string; |
@@ -42,6 +42,4 @@ import { PrivKey } from './abstract/utils.js'; | ||
getSharedSecret: (privateA: PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
signUnhashed: (msg: Uint8Array, privKey: PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
verify: (signature: import("./abstract/utils.js").Hex | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -54,4 +52,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -109,6 +105,4 @@ _bigintToString: (num: bigint) => string; | ||
getSharedSecret: (privateA: PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | import("./abstract/utils.js").Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
sign: (msgHash: import("./abstract/utils.js").Hex, privKey: PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
signUnhashed: (msg: Uint8Array, privKey: PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
verify: (signature: import("./abstract/utils.js").Hex | import("./abstract/weierstrass.js").SignatureType, msgHash: import("./abstract/utils.js").Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -121,4 +115,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -125,0 +117,0 @@ _bigintToString: (num: bigint) => string; |
@@ -43,6 +43,4 @@ import { PointType } from './abstract/weierstrass.js'; | ||
getSharedSecret: (privateA: PrivKey, publicB: import("./abstract/weierstrass.js").PubKey, isCompressed?: boolean | undefined) => Uint8Array; | ||
sign: (msgHash: Hex, privKey: PrivKey, opts?: { | ||
lowS?: boolean | undefined; | ||
extraEntropy?: (true | Hex) | undefined; | ||
} | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
sign: (msgHash: Hex, privKey: PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
signUnhashed: (msg: Uint8Array, privKey: PrivKey, opts?: import("./abstract/weierstrass.js").SignOpts | undefined) => import("./abstract/weierstrass.js").SignatureType; | ||
verify: (signature: Hex | import("./abstract/weierstrass.js").SignatureType, msgHash: Hex, publicKey: import("./abstract/weierstrass.js").PubKey, opts?: { | ||
@@ -55,4 +53,2 @@ lowS?: boolean | undefined; | ||
utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -59,0 +55,0 @@ _bigintToString: (num: bigint) => string; |
@@ -13,6 +13,4 @@ "use strict"; | ||
/** | ||
* secp256k1 belongs to Koblitz curves: it has | ||
* efficiently computable Frobenius endomorphism. | ||
* Endomorphism improves efficiency: | ||
* Uses 2x less RAM, speeds up precomputation by 2x and ECDH / sign key recovery by 20%. | ||
* secp256k1 belongs to Koblitz curves: it has efficiently computable endomorphism. | ||
* Endomorphism uses 2x less RAM, speeds up precomputation by 2x and ECDH / key recovery by 20%. | ||
* Should always be used for Projective's double-and-add multiplication. | ||
@@ -118,3 +116,3 @@ * For affines cached multiplication, it trades off 1/2 init time & 1/3 ram for 20% perf hit. | ||
const b2 = a1; | ||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); | ||
const POW_2_128 = BigInt('0x100000000000000000000000000000000'); // (2n**128n).toString(16) | ||
const c1 = divNearest(b2 * k, n); | ||
@@ -163,18 +161,16 @@ const c2 = divNearest(-b1 * k, n); | ||
// Schnorr is 32 bytes | ||
if (bytes.length === 32) { | ||
const x = (0, utils_js_1.bytesToNumberBE)(bytes); | ||
if (!isValidFieldElement(x)) | ||
throw new Error('Point is not on curve'); | ||
const y2 = exports.secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b | ||
let y = sqrtMod(y2); // y = y² ^ (p+1)/4 | ||
const isYOdd = (y & _1n) === _1n; | ||
// Schnorr | ||
if (isYOdd) | ||
y = exports.secp256k1.CURVE.Fp.negate(y); | ||
const point = new exports.secp256k1.Point(x, y); | ||
point.assertValidity(); | ||
return point; | ||
} | ||
// Do we need that in schnorr at all? | ||
return exports.secp256k1.Point.fromHex(publicKey); | ||
if (bytes.length !== 32) | ||
throw new Error('Schnorr pubkeys must be 32 bytes'); | ||
const x = (0, utils_js_1.bytesToNumberBE)(bytes); | ||
if (!isValidFieldElement(x)) | ||
throw new Error('Point is not on curve'); | ||
const y2 = exports.secp256k1.utils._weierstrassEquation(x); // y² = x³ + ax + b | ||
let y = sqrtMod(y2); // y = y² ^ (p+1)/4 | ||
const isYOdd = (y & _1n) === _1n; | ||
// Schnorr | ||
if (isYOdd) | ||
y = exports.secp256k1.CURVE.Fp.negate(y); | ||
const point = new exports.secp256k1.Point(x, y); | ||
point.assertValidity(); | ||
return point; | ||
} | ||
@@ -216,6 +212,7 @@ } | ||
const bytes = (0, utils_js_1.ensureBytes)(hex); | ||
if (bytes.length !== 64) | ||
throw new TypeError(`SchnorrSignature.fromHex: expected 64 bytes, not ${bytes.length}`); | ||
const r = (0, utils_js_1.bytesToNumberBE)(bytes.subarray(0, 32)); | ||
const s = (0, utils_js_1.bytesToNumberBE)(bytes.subarray(32, 64)); | ||
const len = 32; // group length | ||
if (bytes.length !== 2 * len) | ||
throw new TypeError(`SchnorrSignature.fromHex: expected ${2 * len} bytes, not ${bytes.length}`); | ||
const r = (0, utils_js_1.bytesToNumberBE)(bytes.subarray(0, len)); | ||
const s = (0, utils_js_1.bytesToNumberBE)(bytes.subarray(len, 2 * len)); | ||
return new SchnorrSignature(r, s); | ||
@@ -222,0 +219,0 @@ } |
@@ -46,4 +46,2 @@ import { ProjectivePointType } from './abstract/weierstrass.js'; | ||
export declare const utils: { | ||
mod: (a: bigint, b?: bigint | undefined) => bigint; | ||
invert: (number: bigint, modulo?: bigint | undefined) => bigint; | ||
_bigintToBytes: (num: bigint) => Uint8Array; | ||
@@ -50,0 +48,0 @@ _bigintToString: (num: bigint) => string; |
@@ -137,3 +137,4 @@ "use strict"; | ||
const sha256mask = 2n ** 256n; | ||
const limit = sha256mask - exports.starkCurve.utils.mod(sha256mask, exports.starkCurve.CURVE.n); | ||
const Fn = (0, modular_js_1.Fp)(CURVE.n); | ||
const limit = sha256mask - Fn.create(sha256mask); | ||
for (let i = 0;; i++) { | ||
@@ -143,3 +144,3 @@ const key = hashKeyWithIndex(_seed, i); | ||
if (key < limit) | ||
return exports.starkCurve.utils.mod(key, exports.starkCurve.CURVE.n).toString(16); | ||
return Fn.create(key).toString(16); | ||
} | ||
@@ -146,0 +147,0 @@ } |
{ | ||
"name": "@noble/curves", | ||
"version": "0.5.1", | ||
"version": "0.5.2", | ||
"description": "Minimal, auditable JS implementation of elliptic curve cryptography", | ||
@@ -34,3 +34,3 @@ "files": [ | ||
"micro-bmark": "0.2.0", | ||
"micro-should": "0.2.0", | ||
"micro-should": "0.3.0", | ||
"prettier": "2.6.2", | ||
@@ -37,0 +37,0 @@ "rollup": "2.75.5", |
@@ -204,4 +204,2 @@ # noble-curves | ||
utils: { | ||
mod: (a: bigint, b?: bigint) => bigint; | ||
invert: (number: bigint, modulo?: bigint) => bigint; | ||
randomPrivateKey: () => Uint8Array; | ||
@@ -310,2 +308,3 @@ getExtendedPublicKey: (key: PrivKey) => { | ||
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType; | ||
signUnhashed: (msg: Uint8Array, privKey: PrivKey, opts?: SignOpts) => SignatureType; | ||
verify: ( | ||
@@ -321,4 +320,2 @@ signature: Hex | SignatureType, | ||
utils: { | ||
mod: (a: bigint) => bigint; | ||
invert: (number: bigint) => bigint; | ||
isValidPrivateKey(privateKey: PrivKey): boolean; | ||
@@ -325,0 +322,0 @@ hashToPrivateKey: (hash: Hex) => Uint8Array; |
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
600246
12754
476