Schnorr, weierstrass: refactor
This commit is contained in:
parent
5fc38fc0e7
commit
cffea91061
@ -18,7 +18,7 @@ import { Hex, PrivKey } from './utils.js';
|
|||||||
import * as htf from './hash-to-curve.js';
|
import * as htf from './hash-to-curve.js';
|
||||||
import {
|
import {
|
||||||
CurvePointsType,
|
CurvePointsType,
|
||||||
ProjectivePointType as PPointType,
|
ProjPointType as ProjPointType,
|
||||||
CurvePointsRes,
|
CurvePointsRes,
|
||||||
weierstrassPoints,
|
weierstrassPoints,
|
||||||
AffinePoint,
|
AffinePoint,
|
||||||
@ -27,8 +27,8 @@ import {
|
|||||||
type Fp = bigint; // Can be different field?
|
type Fp = bigint; // Can be different field?
|
||||||
|
|
||||||
export type SignatureCoder<Fp2> = {
|
export type SignatureCoder<Fp2> = {
|
||||||
decode(hex: Hex): PPointType<Fp2>;
|
decode(hex: Hex): ProjPointType<Fp2>;
|
||||||
encode(point: PPointType<Fp2>): Uint8Array;
|
encode(point: ProjPointType<Fp2>): Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
||||||
@ -79,29 +79,29 @@ export type CurveFn<Fp, Fp2, Fp6, Fp12> = {
|
|||||||
G1: ReturnType<(typeof htf.hashToCurve<Fp>)>,
|
G1: ReturnType<(typeof htf.hashToCurve<Fp>)>,
|
||||||
G2: ReturnType<(typeof htf.hashToCurve<Fp2>)>,
|
G2: ReturnType<(typeof htf.hashToCurve<Fp2>)>,
|
||||||
},
|
},
|
||||||
pairing: (P: PPointType<Fp>, Q: PPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
pairing: (P: ProjPointType<Fp>, Q: ProjPointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
||||||
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
getPublicKey: (privateKey: PrivKey) => Uint8Array;
|
||||||
sign: {
|
sign: {
|
||||||
(message: Hex, privateKey: PrivKey): Uint8Array;
|
(message: Hex, privateKey: PrivKey): Uint8Array;
|
||||||
(message: PPointType<Fp2>, privateKey: PrivKey): PPointType<Fp2>;
|
(message: ProjPointType<Fp2>, privateKey: PrivKey): ProjPointType<Fp2>;
|
||||||
};
|
};
|
||||||
verify: (
|
verify: (
|
||||||
signature: Hex | PPointType<Fp2>,
|
signature: Hex | ProjPointType<Fp2>,
|
||||||
message: Hex | PPointType<Fp2>,
|
message: Hex | ProjPointType<Fp2>,
|
||||||
publicKey: Hex | PPointType<Fp>
|
publicKey: Hex | ProjPointType<Fp>
|
||||||
) => boolean;
|
) => boolean;
|
||||||
aggregatePublicKeys: {
|
aggregatePublicKeys: {
|
||||||
(publicKeys: Hex[]): Uint8Array;
|
(publicKeys: Hex[]): Uint8Array;
|
||||||
(publicKeys: PPointType<Fp>[]): PPointType<Fp>;
|
(publicKeys: ProjPointType<Fp>[]): ProjPointType<Fp>;
|
||||||
};
|
};
|
||||||
aggregateSignatures: {
|
aggregateSignatures: {
|
||||||
(signatures: Hex[]): Uint8Array;
|
(signatures: Hex[]): Uint8Array;
|
||||||
(signatures: PPointType<Fp2>[]): PPointType<Fp2>;
|
(signatures: ProjPointType<Fp2>[]): ProjPointType<Fp2>;
|
||||||
};
|
};
|
||||||
verifyBatch: (
|
verifyBatch: (
|
||||||
signature: Hex | PPointType<Fp2>,
|
signature: Hex | ProjPointType<Fp2>,
|
||||||
messages: (Hex | PPointType<Fp2>)[],
|
messages: (Hex | ProjPointType<Fp2>)[],
|
||||||
publicKeys: (Hex | PPointType<Fp>)[]
|
publicKeys: (Hex | ProjPointType<Fp>)[]
|
||||||
) => boolean;
|
) => boolean;
|
||||||
utils: {
|
utils: {
|
||||||
stringToBytes: typeof htf.stringToBytes;
|
stringToBytes: typeof htf.stringToBytes;
|
||||||
|
@ -67,39 +67,39 @@ export type AffinePoint = {
|
|||||||
} & { z?: never; t?: never };
|
} & { z?: never; t?: never };
|
||||||
|
|
||||||
// Instance of Extended Point with coordinates in X, Y, Z, T
|
// Instance of Extended Point with coordinates in X, Y, Z, T
|
||||||
export interface ExtendedPointType extends Group<ExtendedPointType> {
|
export interface ExtPointType extends Group<ExtPointType> {
|
||||||
readonly ex: bigint;
|
readonly ex: bigint;
|
||||||
readonly ey: bigint;
|
readonly ey: bigint;
|
||||||
readonly ez: bigint;
|
readonly ez: bigint;
|
||||||
readonly et: bigint;
|
readonly et: bigint;
|
||||||
multiply(scalar: bigint): ExtendedPointType;
|
multiply(scalar: bigint): ExtPointType;
|
||||||
multiplyUnsafe(scalar: bigint): ExtendedPointType;
|
multiplyUnsafe(scalar: bigint): ExtPointType;
|
||||||
isSmallOrder(): boolean;
|
isSmallOrder(): boolean;
|
||||||
isTorsionFree(): boolean;
|
isTorsionFree(): boolean;
|
||||||
toAffine(iz?: bigint): AffinePoint;
|
toAffine(iz?: bigint): AffinePoint;
|
||||||
clearCofactor(): ExtendedPointType;
|
clearCofactor(): ExtPointType;
|
||||||
}
|
}
|
||||||
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
// Static methods of Extended Point with coordinates in X, Y, Z, T
|
||||||
export interface ExtendedPointConstructor extends GroupConstructor<ExtendedPointType> {
|
export interface ExtPointConstructor extends GroupConstructor<ExtPointType> {
|
||||||
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtendedPointType;
|
new (x: bigint, y: bigint, z: bigint, t: bigint): ExtPointType;
|
||||||
fromAffine(p: AffinePoint): ExtendedPointType;
|
fromAffine(p: AffinePoint): ExtPointType;
|
||||||
fromHex(hex: Hex): ExtendedPointType;
|
fromHex(hex: Hex): ExtPointType;
|
||||||
fromPrivateKey(privateKey: PrivKey): ExtendedPointType; // TODO: remove
|
fromPrivateKey(privateKey: PrivKey): ExtPointType; // TODO: remove
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CurveFn = {
|
export type CurveFn = {
|
||||||
CURVE: ReturnType<typeof validateOpts>;
|
CURVE: ReturnType<typeof validateOpts>;
|
||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
getPublicKey: (privateKey: Hex) => Uint8Array;
|
||||||
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
sign: (message: Hex, privateKey: Hex) => Uint8Array;
|
||||||
verify: (sig: Hex, message: Hex, publicKey: Hex) => boolean;
|
verify: (sig: Hex, message: Hex, publicKey: Hex) => boolean;
|
||||||
ExtendedPoint: ExtendedPointConstructor;
|
ExtendedPoint: ExtPointConstructor;
|
||||||
utils: {
|
utils: {
|
||||||
randomPrivateKey: () => Uint8Array;
|
randomPrivateKey: () => Uint8Array;
|
||||||
getExtendedPublicKey: (key: PrivKey) => {
|
getExtendedPublicKey: (key: Hex) => {
|
||||||
head: Uint8Array;
|
head: Uint8Array;
|
||||||
prefix: Uint8Array;
|
prefix: Uint8Array;
|
||||||
scalar: bigint;
|
scalar: bigint;
|
||||||
point: ExtendedPointType;
|
point: ExtPointType;
|
||||||
pointBytes: Uint8Array;
|
pointBytes: Uint8Array;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -153,20 +153,18 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
// GE = subgroup element, not full group
|
// GE = subgroup element, not full group
|
||||||
return n === _0n ? n : assertGE(n);
|
return n === _0n ? n : assertGE(n);
|
||||||
}
|
}
|
||||||
function badc(a: any) {
|
const coord = (n: bigint) => _0n <= n && n < MASK; // not < P because of ZIP215
|
||||||
return a == null || !ut.big(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pointPrecomputes = new Map<ExtendedPoint, ExtendedPoint[]>();
|
const pointPrecomputes = new Map<Point, Point[]>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
* Extended Point works in extended coordinates: (x, y, z, t) ∋ (x=x/z, y=y/z, t=xy).
|
||||||
* Default Point works in affine coordinates: (x, y)
|
* Default Point works in affine coordinates: (x, y)
|
||||||
* https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
* https://en.wikipedia.org/wiki/Twisted_Edwards_curve#Extended_coordinates
|
||||||
*/
|
*/
|
||||||
class ExtendedPoint implements ExtendedPointType {
|
class Point implements ExtPointType {
|
||||||
static BASE = new ExtendedPoint(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
static readonly BASE = new Point(CURVE.Gx, CURVE.Gy, _1n, modP(CURVE.Gx * CURVE.Gy));
|
||||||
static ZERO = new ExtendedPoint(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
|
static readonly ZERO = new Point(_0n, _1n, _1n, _0n); // 0, 1, 1, 0
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly ex: bigint,
|
readonly ex: bigint,
|
||||||
@ -174,9 +172,10 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
readonly ez: bigint,
|
readonly ez: bigint,
|
||||||
readonly et: bigint
|
readonly et: bigint
|
||||||
) {
|
) {
|
||||||
if (badc(ey)) throw new Error('y required');
|
if (!coord(ex)) throw new Error('x required');
|
||||||
if (badc(ez)) throw new Error('z required');
|
if (!coord(ey)) throw new Error('y required');
|
||||||
if (badc(et)) throw new Error('t required');
|
if (!coord(ez)) throw new Error('z required');
|
||||||
|
if (!coord(et)) throw new Error('t required');
|
||||||
}
|
}
|
||||||
|
|
||||||
get x(): bigint {
|
get x(): bigint {
|
||||||
@ -186,15 +185,15 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
return this.toAffine().y;
|
return this.toAffine().y;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromAffine(p: AffinePoint): ExtendedPoint {
|
static fromAffine(p: AffinePoint): Point {
|
||||||
const { x, y } = p || {};
|
const { x, y } = p || {};
|
||||||
if (p instanceof ExtendedPoint) throw new Error('fromAffine: extended point not allowed');
|
if (p instanceof Point) throw new Error('fromAffine: extended point not allowed');
|
||||||
if (!ut.big(x) || !ut.big(y)) throw new Error('fromAffine: invalid affine point');
|
if (!ut.big(x) || !ut.big(y)) throw new Error('fromAffine: invalid affine point');
|
||||||
return new ExtendedPoint(x, y, _1n, modP(x * y));
|
return new Point(x, y, _1n, modP(x * y));
|
||||||
}
|
}
|
||||||
static normalizeZ(points: ExtendedPoint[]): ExtendedPoint[] {
|
static normalizeZ(points: Point[]): Point[] {
|
||||||
const toInv = Fp.invertBatch(points.map((p) => p.ez));
|
const toInv = Fp.invertBatch(points.map((p) => p.ez));
|
||||||
return points.map((p, i) => p.toAffine(toInv[i])).map(ExtendedPoint.fromAffine);
|
return points.map((p, i) => p.toAffine(toInv[i])).map(Point.fromAffine);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We calculate precomputes for elliptic curve point multiplication
|
// We calculate precomputes for elliptic curve point multiplication
|
||||||
@ -209,7 +208,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compare one point to another.
|
// Compare one point to another.
|
||||||
equals(other: ExtendedPoint): boolean {
|
equals(other: Point): boolean {
|
||||||
assertExtPoint(other);
|
assertExtPoint(other);
|
||||||
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
||||||
const { ex: X2, ey: Y2, ez: Z2 } = other;
|
const { ex: X2, ey: Y2, ez: Z2 } = other;
|
||||||
@ -221,18 +220,18 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected is0(): boolean {
|
protected is0(): boolean {
|
||||||
return this.equals(ExtendedPoint.ZERO);
|
return this.equals(Point.ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inverses point to one corresponding to (x, -y) in Affine coordinates.
|
// Inverses point to one corresponding to (x, -y) in Affine coordinates.
|
||||||
negate(): ExtendedPoint {
|
negate(): Point {
|
||||||
return new ExtendedPoint(modP(-this.ex), this.ey, this.ez, modP(-this.et));
|
return new Point(modP(-this.ex), this.ey, this.ez, modP(-this.et));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast algo for doubling Extended Point.
|
// Fast algo for doubling Extended Point.
|
||||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
||||||
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
// Cost: 4M + 4S + 1*a + 6add + 1*2.
|
||||||
double(): ExtendedPoint {
|
double(): Point {
|
||||||
const { a } = CURVE;
|
const { a } = CURVE;
|
||||||
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
||||||
const A = modP(X1 * X1); // A = X12
|
const A = modP(X1 * X1); // A = X12
|
||||||
@ -248,13 +247,13 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
const Y3 = modP(G * H); // Y3 = G*H
|
const Y3 = modP(G * H); // Y3 = G*H
|
||||||
const T3 = modP(E * H); // T3 = E*H
|
const T3 = modP(E * H); // T3 = E*H
|
||||||
const Z3 = modP(F * G); // Z3 = F*G
|
const Z3 = modP(F * G); // Z3 = F*G
|
||||||
return new ExtendedPoint(X3, Y3, Z3, T3);
|
return new Point(X3, Y3, Z3, T3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast algo for adding 2 Extended Points.
|
// Fast algo for adding 2 Extended Points.
|
||||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd
|
||||||
// Cost: 9M + 1*a + 1*d + 7add.
|
// Cost: 9M + 1*a + 1*d + 7add.
|
||||||
add(other: ExtendedPoint) {
|
add(other: Point) {
|
||||||
assertExtPoint(other);
|
assertExtPoint(other);
|
||||||
const { a, d } = CURVE;
|
const { a, d } = CURVE;
|
||||||
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
||||||
@ -277,7 +276,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
const Y3 = modP(G * H);
|
const Y3 = modP(G * H);
|
||||||
const T3 = modP(E * H);
|
const T3 = modP(E * H);
|
||||||
const Z3 = modP(F * G);
|
const Z3 = modP(F * G);
|
||||||
return new ExtendedPoint(X3, Y3, Z3, T3);
|
return new Point(X3, Y3, Z3, T3);
|
||||||
}
|
}
|
||||||
const A = modP(X1 * X2); // A = X1*X2
|
const A = modP(X1 * X2); // A = X1*X2
|
||||||
const B = modP(Y1 * Y2); // B = Y1*Y2
|
const B = modP(Y1 * Y2); // B = Y1*Y2
|
||||||
@ -292,29 +291,29 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
const T3 = modP(E * H); // T3 = E*H
|
const T3 = modP(E * H); // T3 = E*H
|
||||||
const Z3 = modP(F * G); // Z3 = F*G
|
const Z3 = modP(F * G); // Z3 = F*G
|
||||||
|
|
||||||
return new ExtendedPoint(X3, Y3, Z3, T3);
|
return new Point(X3, Y3, Z3, T3);
|
||||||
}
|
}
|
||||||
|
|
||||||
subtract(other: ExtendedPoint): ExtendedPoint {
|
subtract(other: Point): Point {
|
||||||
return this.add(other.negate());
|
return this.add(other.negate());
|
||||||
}
|
}
|
||||||
|
|
||||||
private wNAF(n: bigint): { p: ExtendedPoint; f: ExtendedPoint } {
|
private wNAF(n: bigint): { p: Point; f: Point } {
|
||||||
return wnaf.wNAFCached(this, pointPrecomputes, n, ExtendedPoint.normalizeZ);
|
return wnaf.wNAFCached(this, pointPrecomputes, n, Point.normalizeZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constant time multiplication.
|
// Constant time multiplication.
|
||||||
// Uses wNAF method. Windowed method may be 10% faster,
|
// Uses wNAF method. Windowed method may be 10% faster,
|
||||||
// but takes 2x longer to generate and consumes 2x memory.
|
// but takes 2x longer to generate and consumes 2x memory.
|
||||||
multiply(scalar: bigint): ExtendedPoint {
|
multiply(scalar: bigint): Point {
|
||||||
const { p, f } = this.wNAF(assertGE(scalar));
|
const { p, f } = this.wNAF(assertGE(scalar));
|
||||||
return ExtendedPoint.normalizeZ([p, f])[0];
|
return Point.normalizeZ([p, f])[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
// Non-constant-time multiplication. Uses double-and-add algorithm.
|
||||||
// It's faster, but should only be used when you don't care about
|
// It's faster, but should only be used when you don't care about
|
||||||
// an exposed private key e.g. sig verification.
|
// an exposed private key e.g. sig verification.
|
||||||
multiplyUnsafe(scalar: bigint): ExtendedPoint {
|
multiplyUnsafe(scalar: bigint): Point {
|
||||||
let n = assertGE0(scalar);
|
let n = assertGE0(scalar);
|
||||||
if (n === _0n) return I;
|
if (n === _0n) return I;
|
||||||
if (this.equals(I) || n === _1n) return this;
|
if (this.equals(I) || n === _1n) return this;
|
||||||
@ -349,7 +348,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
if (zz !== _1n) throw new Error('invZ was invalid');
|
if (zz !== _1n) throw new Error('invZ was invalid');
|
||||||
return { x: ax, y: ay };
|
return { x: ax, y: ay };
|
||||||
}
|
}
|
||||||
clearCofactor(): ExtendedPoint {
|
clearCofactor(): Point {
|
||||||
const { h: cofactor } = CURVE;
|
const { h: cofactor } = CURVE;
|
||||||
if (cofactor === _1n) return this;
|
if (cofactor === _1n) return this;
|
||||||
return this.multiplyUnsafe(cofactor);
|
return this.multiplyUnsafe(cofactor);
|
||||||
@ -399,7 +398,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
const isXOdd = (x & _1n) === _1n;
|
const isXOdd = (x & _1n) === _1n;
|
||||||
const isLastByteOdd = (lastByte & 0x80) !== 0;
|
const isLastByteOdd = (lastByte & 0x80) !== 0;
|
||||||
if (isLastByteOdd !== isXOdd) x = modP(-x);
|
if (isLastByteOdd !== isXOdd) x = modP(-x);
|
||||||
return ExtendedPoint.fromAffine({ x, y });
|
return Point.fromAffine({ x, y });
|
||||||
}
|
}
|
||||||
static fromPrivateKey(privateKey: PrivKey) {
|
static fromPrivateKey(privateKey: PrivKey) {
|
||||||
return getExtendedPublicKey(privateKey).point;
|
return getExtendedPublicKey(privateKey).point;
|
||||||
@ -415,11 +414,11 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
return ut.bytesToHex(this.toRawBytes()); // Same as toRawBytes, but returns string.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const { BASE: G, ZERO: I } = ExtendedPoint;
|
const { BASE: G, ZERO: I } = Point;
|
||||||
const wnaf = wNAF(ExtendedPoint, CURVE.nByteLength * 8);
|
const wnaf = wNAF(Point, CURVE.nByteLength * 8);
|
||||||
|
|
||||||
function assertExtPoint(other: unknown) {
|
function assertExtPoint(other: unknown) {
|
||||||
if (!(other instanceof ExtendedPoint)) throw new Error('ExtendedPoint expected');
|
if (!(other instanceof Point)) throw new Error('ExtendedPoint expected');
|
||||||
}
|
}
|
||||||
// Little-endian SHA512 with modulo n
|
// Little-endian SHA512 with modulo n
|
||||||
function modnLE(hash: Uint8Array): bigint {
|
function modnLE(hash: Uint8Array): bigint {
|
||||||
@ -490,15 +489,15 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
sig = ensureBytes(sig, 2 * len);
|
sig = ensureBytes(sig, 2 * len);
|
||||||
message = ensureBytes(message);
|
message = ensureBytes(message);
|
||||||
if (CURVE.preHash) message = CURVE.preHash(message);
|
if (CURVE.preHash) message = CURVE.preHash(message);
|
||||||
const R = ExtendedPoint.fromHex(sig.slice(0, len), false); // non-strict; allows 0..MASK
|
const R = Point.fromHex(sig.slice(0, len), false); // non-strict; allows 0..MASK
|
||||||
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
|
const s = ut.bytesToNumberLE(sig.slice(len, 2 * len));
|
||||||
const A = ExtendedPoint.fromHex(publicKey, false); // Check for s bounds, hex validity
|
const A = Point.fromHex(publicKey, false); // Check for s bounds, hex validity
|
||||||
const SB = G.multiplyUnsafe(s);
|
const SB = G.multiplyUnsafe(s);
|
||||||
const k = hashDomainToScalar(ut.concatBytes(R.toRawBytes(), A.toRawBytes(), message), context);
|
const k = hashDomainToScalar(ut.concatBytes(R.toRawBytes(), A.toRawBytes(), message), context);
|
||||||
const kA = A.multiplyUnsafe(k);
|
const kA = A.multiplyUnsafe(k);
|
||||||
const RkA = R.add(kA);
|
const RkA = R.add(kA);
|
||||||
// [8][S]B = [8]R + [8][k]A'
|
// [8][S]B = [8]R + [8][k]A'
|
||||||
return RkA.subtract(SB).clearCofactor().equals(ExtendedPoint.ZERO);
|
return RkA.subtract(SB).clearCofactor().equals(Point.ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||||
@ -523,7 +522,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
|
* but allows to speed-up subsequent getPublicKey() calls up to 20x.
|
||||||
* @param windowSize 2, 4, 8, 16
|
* @param windowSize 2, 4, 8, 16
|
||||||
*/
|
*/
|
||||||
precompute(windowSize = 8, point = ExtendedPoint.BASE): typeof ExtendedPoint.BASE {
|
precompute(windowSize = 8, point = Point.BASE): typeof Point.BASE {
|
||||||
point._setWindowSize(windowSize);
|
point._setWindowSize(windowSize);
|
||||||
point.multiply(BigInt(3));
|
point.multiply(BigInt(3));
|
||||||
return point;
|
return point;
|
||||||
@ -535,7 +534,7 @@ export function twistedEdwards(curveDef: CurveType): CurveFn {
|
|||||||
getPublicKey,
|
getPublicKey,
|
||||||
sign,
|
sign,
|
||||||
verify,
|
verify,
|
||||||
ExtendedPoint,
|
ExtendedPoint: Point,
|
||||||
utils,
|
utils,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -32,12 +32,9 @@ export type BasicCurve<T> = ut.BasicCurve<T> & {
|
|||||||
endo?: EndomorphismOpts;
|
endo?: EndomorphismOpts;
|
||||||
// When a cofactor != 1, there can be an effective methods to:
|
// When a cofactor != 1, there can be an effective methods to:
|
||||||
// 1. Determine whether a point is torsion-free
|
// 1. Determine whether a point is torsion-free
|
||||||
isTorsionFree?: (c: ProjectiveConstructor<T>, point: ProjectivePointType<T>) => boolean;
|
isTorsionFree?: (c: ProjConstructor<T>, point: ProjPointType<T>) => boolean;
|
||||||
// 2. Clear torsion component
|
// 2. Clear torsion component
|
||||||
clearCofactor?: (
|
clearCofactor?: (c: ProjConstructor<T>, point: ProjPointType<T>) => ProjPointType<T>;
|
||||||
c: ProjectiveConstructor<T>,
|
|
||||||
point: ProjectivePointType<T>
|
|
||||||
) => ProjectivePointType<T>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ASN.1 DER encoding utilities
|
// ASN.1 DER encoding utilities
|
||||||
@ -115,43 +112,35 @@ export type AffinePoint<T> = {
|
|||||||
y: T;
|
y: T;
|
||||||
} & { z?: never };
|
} & { z?: never };
|
||||||
// Instance for 3d XYZ points
|
// Instance for 3d XYZ points
|
||||||
export interface ProjectivePointType<T> extends Group<ProjectivePointType<T>> {
|
export interface ProjPointType<T> extends Group<ProjPointType<T>> {
|
||||||
readonly px: T;
|
readonly px: T;
|
||||||
readonly py: T;
|
readonly py: T;
|
||||||
readonly pz: T;
|
readonly pz: T;
|
||||||
multiply(scalar: bigint): ProjectivePointType<T>;
|
multiply(scalar: bigint): ProjPointType<T>;
|
||||||
multiplyUnsafe(scalar: bigint): ProjectivePointType<T>;
|
multiplyUnsafe(scalar: bigint): ProjPointType<T>;
|
||||||
multiplyAndAddUnsafe(
|
multiplyAndAddUnsafe(Q: ProjPointType<T>, a: bigint, b: bigint): ProjPointType<T> | undefined;
|
||||||
Q: ProjectivePointType<T>,
|
|
||||||
a: bigint,
|
|
||||||
b: bigint
|
|
||||||
): ProjectivePointType<T> | undefined;
|
|
||||||
_setWindowSize(windowSize: number): void;
|
_setWindowSize(windowSize: number): void;
|
||||||
toAffine(iz?: T): AffinePoint<T>;
|
toAffine(iz?: T): AffinePoint<T>;
|
||||||
isTorsionFree(): boolean;
|
isTorsionFree(): boolean;
|
||||||
clearCofactor(): ProjectivePointType<T>;
|
clearCofactor(): ProjPointType<T>;
|
||||||
assertValidity(): void;
|
assertValidity(): void;
|
||||||
hasEvenY(): boolean;
|
hasEvenY(): boolean;
|
||||||
toRawBytes(isCompressed?: boolean): Uint8Array;
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
toHex(isCompressed?: boolean): string;
|
toHex(isCompressed?: boolean): string;
|
||||||
}
|
}
|
||||||
// Static methods for 3d XYZ points
|
// Static methods for 3d XYZ points
|
||||||
export interface ProjectiveConstructor<T> extends GroupConstructor<ProjectivePointType<T>> {
|
export interface ProjConstructor<T> extends GroupConstructor<ProjPointType<T>> {
|
||||||
new (x: T, y: T, z: T): ProjectivePointType<T>;
|
new (x: T, y: T, z: T): ProjPointType<T>;
|
||||||
fromAffine(p: AffinePoint<T>): ProjectivePointType<T>;
|
fromAffine(p: AffinePoint<T>): ProjPointType<T>;
|
||||||
fromHex(hex: Hex): ProjectivePointType<T>;
|
fromHex(hex: Hex): ProjPointType<T>;
|
||||||
fromPrivateKey(privateKey: PrivKey): ProjectivePointType<T>;
|
fromPrivateKey(privateKey: PrivKey): ProjPointType<T>;
|
||||||
normalizeZ(points: ProjectivePointType<T>[]): ProjectivePointType<T>[];
|
normalizeZ(points: ProjPointType<T>[]): ProjPointType<T>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CurvePointsType<T> = BasicCurve<T> & {
|
export type CurvePointsType<T> = BasicCurve<T> & {
|
||||||
// Bytes
|
// Bytes
|
||||||
fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
|
fromBytes: (bytes: Uint8Array) => AffinePoint<T>;
|
||||||
toBytes: (
|
toBytes: (c: ProjConstructor<T>, point: ProjPointType<T>, compressed: boolean) => Uint8Array;
|
||||||
c: ProjectiveConstructor<T>,
|
|
||||||
point: ProjectivePointType<T>,
|
|
||||||
compressed: boolean
|
|
||||||
) => Uint8Array;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
||||||
@ -185,7 +174,7 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type CurvePointsRes<T> = {
|
export type CurvePointsRes<T> = {
|
||||||
ProjectivePoint: ProjectiveConstructor<T>;
|
ProjectivePoint: ProjConstructor<T>;
|
||||||
normalizePrivateKey: (key: PrivKey) => bigint;
|
normalizePrivateKey: (key: PrivKey) => bigint;
|
||||||
weierstrassEquation: (x: T) => T;
|
weierstrassEquation: (x: T) => T;
|
||||||
isWithinCurveOrder: (num: bigint) => boolean;
|
isWithinCurveOrder: (num: bigint) => boolean;
|
||||||
@ -230,17 +219,15 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
if (typeof key === 'bigint') {
|
if (typeof key === 'bigint') {
|
||||||
// Curve order check is done below
|
// Curve order check is done below
|
||||||
num = key;
|
num = key;
|
||||||
} else if (ut.isPositiveInt(key)) {
|
|
||||||
num = BigInt(key);
|
|
||||||
} else if (typeof key === 'string') {
|
} else if (typeof key === 'string') {
|
||||||
if (key.length !== 2 * groupLen) throw new Error(`Private key must be ${groupLen} bytes`);
|
if (key.length !== 2 * groupLen) throw new Error(`must be ${groupLen} bytes`);
|
||||||
// Validates individual octets
|
// Validates individual octets
|
||||||
num = ut.hexToNumber(key);
|
num = ut.hexToNumber(key);
|
||||||
} else if (key instanceof Uint8Array) {
|
} else if (key instanceof Uint8Array) {
|
||||||
if (key.length !== groupLen) throw new Error(`Private key must be ${groupLen} bytes`);
|
if (key.length !== groupLen) throw new Error(`must be ${groupLen} bytes`);
|
||||||
num = ut.bytesToNumberBE(key);
|
num = ut.bytesToNumberBE(key);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Private key was invalid');
|
throw new Error('private key must be bytes, hex or bigint, not ' + typeof key);
|
||||||
}
|
}
|
||||||
// Useful for curves with cofactor != 1
|
// Useful for curves with cofactor != 1
|
||||||
if (wrapPrivateKey) num = mod.mod(num, order);
|
if (wrapPrivateKey) num = mod.mod(num, order);
|
||||||
@ -249,25 +236,28 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pointPrecomputes = new Map<ProjectivePoint, ProjectivePoint[]>();
|
const pointPrecomputes = new Map<ProjectivePoint, ProjectivePoint[]>();
|
||||||
|
function assertPrjPoint(other: unknown) {
|
||||||
|
if (!(other instanceof ProjectivePoint)) throw new Error('ProjectivePoint expected');
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
* Projective Point works in 3d / projective (homogeneous) coordinates: (x, y, z) ∋ (x=x/z, y=y/z)
|
||||||
* Default Point works in 2d / affine coordinates: (x, y)
|
* Default Point works in 2d / affine coordinates: (x, y)
|
||||||
* We're doing calculations in projective, because its operations don't require costly inversion.
|
* We're doing calculations in projective, because its operations don't require costly inversion.
|
||||||
*/
|
*/
|
||||||
class ProjectivePoint implements ProjectivePointType<T> {
|
class ProjectivePoint implements ProjPointType<T> {
|
||||||
static readonly BASE = new ProjectivePoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
static readonly BASE = new ProjectivePoint(CURVE.Gx, CURVE.Gy, Fp.ONE);
|
||||||
static readonly ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
static readonly ZERO = new ProjectivePoint(Fp.ZERO, Fp.ONE, Fp.ZERO);
|
||||||
|
|
||||||
constructor(readonly px: T, readonly py: T, readonly pz: T) {
|
constructor(readonly px: T, readonly py: T, readonly pz: T) {
|
||||||
if (py == null || !Fp.isValid(py)) throw new Error('ProjectivePoint: y required');
|
if (px == null || !Fp.isValid(px)) throw new Error('x required');
|
||||||
if (pz == null || !Fp.isValid(pz)) throw new Error('ProjectivePoint: z required');
|
if (py == null || !Fp.isValid(py)) throw new Error('y required');
|
||||||
|
if (pz == null || !Fp.isValid(pz)) throw new Error('z required');
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromAffine(p: AffinePoint<T>): ProjectivePoint {
|
static fromAffine(p: AffinePoint<T>): ProjectivePoint {
|
||||||
const { x, y } = p || {};
|
const { x, y } = p || {};
|
||||||
if (!p || !Fp.isValid(x) || !Fp.isValid(y))
|
if (!p || !Fp.isValid(x) || !Fp.isValid(y)) throw new Error('invalid affine point');
|
||||||
throw new Error('fromAffine: invalid affine point');
|
if (p instanceof ProjectivePoint) throw new Error('projective point not allowed');
|
||||||
if (p instanceof ProjectivePoint) throw new Error('fromAffine: projective point not allowed');
|
|
||||||
const is0 = (i: T) => Fp.equals(i, Fp.ZERO);
|
const is0 = (i: T) => Fp.equals(i, Fp.ZERO);
|
||||||
// fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
|
// fromAffine(x:0, y:0) would produce (x:0, y:0, z:1), but we need (x:0, y:1, z:0)
|
||||||
if (is0(x) && is0(y)) return ProjectivePoint.ZERO;
|
if (is0(x) && is0(y)) return ProjectivePoint.ZERO;
|
||||||
@ -587,11 +577,8 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
const _bits = CURVE.nBitLength;
|
const _bits = CURVE.nBitLength;
|
||||||
const wnaf = wNAF(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
const wnaf = wNAF(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
||||||
|
|
||||||
function assertPrjPoint(other: unknown) {
|
|
||||||
if (!(other instanceof ProjectivePoint)) throw new Error('ProjectivePoint expected');
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
ProjectivePoint: ProjectivePoint as ProjectiveConstructor<T>,
|
ProjectivePoint: ProjectivePoint as ProjConstructor<T>,
|
||||||
normalizePrivateKey,
|
normalizePrivateKey,
|
||||||
weierstrassEquation,
|
weierstrassEquation,
|
||||||
isWithinCurveOrder,
|
isWithinCurveOrder,
|
||||||
@ -607,7 +594,7 @@ export interface SignatureType {
|
|||||||
addRecoveryBit(recovery: number): SignatureType;
|
addRecoveryBit(recovery: number): SignatureType;
|
||||||
hasHighS(): boolean;
|
hasHighS(): boolean;
|
||||||
normalizeS(): SignatureType;
|
normalizeS(): SignatureType;
|
||||||
recoverPublicKey(msgHash: Hex): ProjectivePointType<bigint>;
|
recoverPublicKey(msgHash: Hex): ProjPointType<bigint>;
|
||||||
// DER-encoded
|
// DER-encoded
|
||||||
toDERRawBytes(isCompressed?: boolean): Uint8Array;
|
toDERRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
toDERHex(isCompressed?: boolean): string;
|
toDERHex(isCompressed?: boolean): string;
|
||||||
@ -621,7 +608,7 @@ export type SignatureConstructor = {
|
|||||||
fromDER(hex: Hex): SignatureType;
|
fromDER(hex: Hex): SignatureType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PubKey = Hex | ProjectivePointType<bigint>;
|
export type PubKey = Hex | ProjPointType<bigint>;
|
||||||
|
|
||||||
export type CurveType = BasicCurve<bigint> & {
|
export type CurveType = BasicCurve<bigint> & {
|
||||||
// Default options
|
// Default options
|
||||||
@ -651,7 +638,7 @@ export type CurveFn = {
|
|||||||
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
getSharedSecret: (privateA: PrivKey, publicB: Hex, isCompressed?: boolean) => Uint8Array;
|
||||||
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
sign: (msgHash: Hex, privKey: PrivKey, opts?: SignOpts) => SignatureType;
|
||||||
verify: (signature: Hex | SignatureType, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
|
verify: (signature: Hex | SignatureType, msgHash: Hex, publicKey: Hex, opts?: VerOpts) => boolean;
|
||||||
ProjectivePoint: ProjectiveConstructor<bigint>;
|
ProjectivePoint: ProjConstructor<bigint>;
|
||||||
Signature: SignatureConstructor;
|
Signature: SignatureConstructor;
|
||||||
utils: {
|
utils: {
|
||||||
_bigintToBytes: (num: bigint) => Uint8Array;
|
_bigintToBytes: (num: bigint) => Uint8Array;
|
||||||
@ -1104,7 +1091,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
publicKey: Hex,
|
publicKey: Hex,
|
||||||
opts = defaultVerOpts
|
opts = defaultVerOpts
|
||||||
): boolean {
|
): boolean {
|
||||||
let P: ProjectivePointType<bigint>;
|
let P: ProjPointType<bigint>;
|
||||||
let _sig: Signature | undefined = undefined;
|
let _sig: Signature | undefined = undefined;
|
||||||
if (publicKey instanceof Point) throw new Error('publicKey must be hex');
|
if (publicKey instanceof Point) throw new Error('publicKey must be hex');
|
||||||
try {
|
try {
|
||||||
|
@ -28,8 +28,8 @@ import {
|
|||||||
} from './abstract/utils.js';
|
} from './abstract/utils.js';
|
||||||
// Types
|
// Types
|
||||||
import {
|
import {
|
||||||
ProjectivePointType,
|
ProjPointType,
|
||||||
ProjectiveConstructor,
|
ProjConstructor,
|
||||||
mapToCurveSimpleSWU,
|
mapToCurveSimpleSWU,
|
||||||
AffinePoint,
|
AffinePoint,
|
||||||
} from './abstract/weierstrass.js';
|
} from './abstract/weierstrass.js';
|
||||||
@ -886,7 +886,7 @@ function psi(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
|||||||
return [x2, y2];
|
return [x2, y2];
|
||||||
}
|
}
|
||||||
// Ψ endomorphism
|
// Ψ endomorphism
|
||||||
function G2psi(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
|
function G2psi(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
||||||
const affine = P.toAffine();
|
const affine = P.toAffine();
|
||||||
const p = psi(affine.x, affine.y);
|
const p = psi(affine.x, affine.y);
|
||||||
return new c(p[0], p[1], Fp2.ONE);
|
return new c(p[0], p[1], Fp2.ONE);
|
||||||
@ -899,7 +899,7 @@ const PSI2_C1 =
|
|||||||
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
function psi2(x: Fp2, y: Fp2): [Fp2, Fp2] {
|
||||||
return [Fp2.mul(x, PSI2_C1), Fp2.negate(y)];
|
return [Fp2.mul(x, PSI2_C1), Fp2.negate(y)];
|
||||||
}
|
}
|
||||||
function G2psi2(c: ProjectiveConstructor<Fp2>, P: ProjectivePointType<Fp2>) {
|
function G2psi2(c: ProjConstructor<Fp2>, P: ProjPointType<Fp2>) {
|
||||||
const affine = P.toAffine();
|
const affine = P.toAffine();
|
||||||
const p = psi2(affine.x, affine.y);
|
const p = psi2(affine.x, affine.y);
|
||||||
return new c(p[0], p[1], Fp2.ONE);
|
return new c(p[0], p[1], Fp2.ONE);
|
||||||
@ -1190,7 +1190,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
},
|
},
|
||||||
Signature: {
|
Signature: {
|
||||||
// TODO: Optimize, it's very slow because of sqrt.
|
// TODO: Optimize, it's very slow because of sqrt.
|
||||||
decode(hex: Hex): ProjectivePointType<Fp2> {
|
decode(hex: Hex): ProjPointType<Fp2> {
|
||||||
hex = ensureBytes(hex);
|
hex = ensureBytes(hex);
|
||||||
const P = Fp.ORDER;
|
const P = Fp.ORDER;
|
||||||
const half = hex.length / 2;
|
const half = hex.length / 2;
|
||||||
@ -1222,7 +1222,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
point.assertValidity();
|
point.assertValidity();
|
||||||
return point;
|
return point;
|
||||||
},
|
},
|
||||||
encode(point: ProjectivePointType<Fp2>) {
|
encode(point: ProjPointType<Fp2>) {
|
||||||
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
// NOTE: by some reasons it was missed in bls12-381, looks like bug
|
||||||
point.assertValidity();
|
point.assertValidity();
|
||||||
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
|
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { sha512 } from '@noble/hashes/sha512';
|
import { sha512 } from '@noble/hashes/sha512';
|
||||||
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
import { concatBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils';
|
||||||
import { twistedEdwards, ExtendedPointType } from './abstract/edwards.js';
|
import { twistedEdwards, ExtPointType } from './abstract/edwards.js';
|
||||||
import { montgomery } from './abstract/montgomery.js';
|
import { montgomery } from './abstract/montgomery.js';
|
||||||
import { mod, pow2, isNegativeLE, Fp as Field, FpSqrtEven } from './abstract/modular.js';
|
import { mod, pow2, isNegativeLE, Fp as Field, FpSqrtEven } from './abstract/modular.js';
|
||||||
import {
|
import {
|
||||||
@ -269,7 +269,7 @@ const MAX_255B = BigInt('0x7ffffffffffffffffffffffffffffffffffffffffffffffffffff
|
|||||||
const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
const bytes255ToNumberLE = (bytes: Uint8Array) =>
|
||||||
ed25519.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
|
ed25519.CURVE.Fp.create(bytesToNumberLE(bytes) & MAX_255B);
|
||||||
|
|
||||||
type ExtendedPoint = ExtendedPointType;
|
type ExtendedPoint = ExtPointType;
|
||||||
|
|
||||||
// Computes Elligator map for Ristretto
|
// Computes Elligator map for Ristretto
|
||||||
// https://ristretto.group/formulas/elligator.html
|
// https://ristretto.group/formulas/elligator.html
|
||||||
|
270
src/secp256k1.ts
270
src/secp256k1.ts
@ -2,15 +2,8 @@
|
|||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { Fp as Field, mod, pow2 } from './abstract/modular.js';
|
import { Fp as Field, mod, pow2 } from './abstract/modular.js';
|
||||||
import { createCurve } from './_shortw_utils.js';
|
import { createCurve } from './_shortw_utils.js';
|
||||||
import { ProjectivePointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
import { ProjPointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
import {
|
import { ensureBytes, concatBytes, Hex, bytesToNumberBE, PrivKey } from './abstract/utils.js';
|
||||||
ensureBytes,
|
|
||||||
concatBytes,
|
|
||||||
Hex,
|
|
||||||
hexToBytes,
|
|
||||||
bytesToNumberBE,
|
|
||||||
PrivKey,
|
|
||||||
} from './abstract/utils.js';
|
|
||||||
import { randomBytes } from '@noble/hashes/utils';
|
import { randomBytes } from '@noble/hashes/utils';
|
||||||
import * as htf from './abstract/hash-to-curve.js';
|
import * as htf from './abstract/hash-to-curve.js';
|
||||||
|
|
||||||
@ -62,45 +55,6 @@ function sqrtMod(y: bigint): bigint {
|
|||||||
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
const Fp = Field(secp256k1P, undefined, undefined, { sqrt: sqrtMod });
|
||||||
type Fp = bigint;
|
type Fp = bigint;
|
||||||
|
|
||||||
const isoMap = htf.isogenyMap(
|
|
||||||
Fp,
|
|
||||||
[
|
|
||||||
// xNum
|
|
||||||
[
|
|
||||||
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
|
|
||||||
'0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
|
|
||||||
'0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
|
|
||||||
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
|
|
||||||
],
|
|
||||||
// xDen
|
|
||||||
[
|
|
||||||
'0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
|
|
||||||
'0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
|
|
||||||
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
||||||
],
|
|
||||||
// yNum
|
|
||||||
[
|
|
||||||
'0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
|
|
||||||
'0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
|
|
||||||
'0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
|
|
||||||
'0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
|
|
||||||
],
|
|
||||||
// yDen
|
|
||||||
[
|
|
||||||
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
|
|
||||||
'0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
|
|
||||||
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
|
|
||||||
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
|
||||||
],
|
|
||||||
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]]
|
|
||||||
);
|
|
||||||
|
|
||||||
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
|
||||||
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
|
||||||
B: BigInt('1771'),
|
|
||||||
Z: Fp.create(BigInt('-11')),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const secp256k1 = createCurve(
|
export const secp256k1 = createCurve(
|
||||||
{
|
{
|
||||||
// Params: a, b
|
// Params: a, b
|
||||||
@ -147,54 +101,11 @@ export const secp256k1 = createCurve(
|
|||||||
sha256
|
sha256
|
||||||
);
|
);
|
||||||
|
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
|
||||||
secp256k1.ProjectivePoint,
|
|
||||||
(scalars: bigint[]) => {
|
|
||||||
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
|
||||||
return isoMap(x, y);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
|
||||||
encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
|
|
||||||
p: Fp.ORDER,
|
|
||||||
m: 1,
|
|
||||||
k: 128,
|
|
||||||
expand: 'xmd',
|
|
||||||
hash: sha256,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
export { hashToCurve, encodeToCurve };
|
|
||||||
|
|
||||||
// Schnorr
|
// Schnorr
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const numTo32b = secp256k1.utils._bigintToBytes;
|
const numTo32b = secp256k1.utils._bigintToBytes;
|
||||||
const numTo32bStr = secp256k1.utils._bigintToString;
|
const fe = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1P;
|
||||||
const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
|
const ge = (x: bigint) => typeof x === 'bigint' && _0n < x && x < secp256k1N;
|
||||||
|
|
||||||
// TODO: export?
|
|
||||||
function normalizePublicKey(publicKey: Hex | PointType<bigint>): PointType<bigint> {
|
|
||||||
if (publicKey instanceof secp256k1.ProjectivePoint) {
|
|
||||||
publicKey.assertValidity();
|
|
||||||
return publicKey;
|
|
||||||
} else {
|
|
||||||
const bytes = ensureBytes(publicKey);
|
|
||||||
// Schnorr is 32 bytes
|
|
||||||
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 = secp256k1.ProjectivePoint.fromAffine({ x, y });
|
|
||||||
point.assertValidity();
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isWithinCurveOrder = secp256k1.utils._isWithinCurveOrder;
|
|
||||||
const isValidFieldElement = secp256k1.utils._isValidFieldElement;
|
|
||||||
|
|
||||||
const TAGS = {
|
const TAGS = {
|
||||||
challenge: 'BIP0340/challenge',
|
challenge: 'BIP0340/challenge',
|
||||||
@ -213,45 +124,44 @@ export function taggedHash(tag: string, ...messages: Uint8Array[]): Uint8Array {
|
|||||||
}
|
}
|
||||||
return sha256(concatBytes(tagP, ...messages));
|
return sha256(concatBytes(tagP, ...messages));
|
||||||
}
|
}
|
||||||
|
|
||||||
const toRawX = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
|
||||||
|
|
||||||
// Schnorr signatures are superior to ECDSA from above.
|
// Schnorr signatures are superior to ECDSA from above.
|
||||||
// Below is Schnorr-specific code as per BIP0340.
|
// Below is Schnorr-specific code as per BIP0340.
|
||||||
function schnorrChallengeFinalize(ch: Uint8Array): bigint {
|
|
||||||
return mod(bytesToNumberBE(ch), secp256k1.CURVE.n);
|
|
||||||
}
|
|
||||||
// Do we need this at all for Schnorr?
|
|
||||||
class SchnorrSignature {
|
|
||||||
constructor(readonly r: bigint, readonly s: bigint) {
|
|
||||||
this.assertValidity();
|
|
||||||
}
|
|
||||||
static fromHex(hex: Hex) {
|
|
||||||
const bytes = ensureBytes(hex);
|
|
||||||
const len = 32; // group length
|
|
||||||
if (bytes.length !== 2 * len)
|
|
||||||
throw new Error(`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);
|
|
||||||
}
|
|
||||||
assertValidity() {
|
|
||||||
const { r, s } = this;
|
|
||||||
if (!isValidFieldElement(r) || !isWithinCurveOrder(s)) throw new Error('Invalid signature');
|
|
||||||
}
|
|
||||||
toHex(): string {
|
|
||||||
return numTo32bStr(this.r) + numTo32bStr(this.s);
|
|
||||||
}
|
|
||||||
toRawBytes(): Uint8Array {
|
|
||||||
return hexToBytes(this.toHex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const tag = taggedHash;
|
||||||
|
const toRawX = (point: PointType<bigint>) => point.toRawBytes(true).slice(1);
|
||||||
|
const b2num = bytesToNumberBE;
|
||||||
|
const modN = (x: bigint) => mod(x, secp256k1N);
|
||||||
|
function validateRS(r: bigint, s: bigint) {
|
||||||
|
if (!fe(r) || !ge(s)) throw new Error('Invalid signature');
|
||||||
|
}
|
||||||
|
const PPoint = secp256k1.ProjectivePoint;
|
||||||
function schnorrGetScalar(priv: bigint) {
|
function schnorrGetScalar(priv: bigint) {
|
||||||
const point = secp256k1.ProjectivePoint.fromPrivateKey(priv);
|
const point = PPoint.fromPrivateKey(priv);
|
||||||
const scalar = point.hasEvenY() ? priv : secp256k1.CURVE.n - priv;
|
const scalar = point.hasEvenY() ? priv : modN(-priv);
|
||||||
return { point, scalar, x: toRawX(point) };
|
return { point, scalar, x: toRawX(point) };
|
||||||
}
|
}
|
||||||
|
function lift_x(x: bigint) {
|
||||||
|
if (!fe(x)) throw new Error('not fe'); // Fail if x ≥ p.
|
||||||
|
const c = mod(x * x * x + BigInt(7), secp256k1P); // Let c = x3 + 7 mod p.
|
||||||
|
let y = sqrtMod(c); // Let y = c^(p+1)/4 mod p.
|
||||||
|
if (y % 2n !== 0n) y = mod(-y, secp256k1P); // Return the unique point P such that x(P) = x and
|
||||||
|
const p = new PPoint(x, y, _1n); // y(P) = y if y mod 2 = 0 or y(P) = p-y otherwise.
|
||||||
|
p.assertValidity();
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
function packSig(r: bigint, s: bigint): Uint8Array {
|
||||||
|
validateRS(r, s);
|
||||||
|
const sig = new Uint8Array(64);
|
||||||
|
sig.set(numTo32b(r), 0);
|
||||||
|
sig.set(numTo32b(s), 32);
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
function unpackSig(sig: Uint8Array): { r: bigint; s: bigint } {
|
||||||
|
const r = b2num(sig.subarray(0, 32)); // Let r = int(sig[0:32]); fail if r ≥ p.
|
||||||
|
const s = b2num(sig.subarray(32, 64)); // Let s = int(sig[32:64]); fail if s ≥ n.
|
||||||
|
validateRS(r, s);
|
||||||
|
return { r, s };
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Synchronously creates Schnorr signature. Improved security: verifies itself before
|
* Synchronously creates Schnorr signature. Improved security: verifies itself before
|
||||||
* producing an output.
|
* producing an output.
|
||||||
@ -259,26 +169,20 @@ function schnorrGetScalar(priv: bigint) {
|
|||||||
* @param privateKey private key
|
* @param privateKey private key
|
||||||
* @param auxRand random bytes that would be added to k. Bad RNG won't break it.
|
* @param auxRand random bytes that would be added to k. Bad RNG won't break it.
|
||||||
*/
|
*/
|
||||||
function schnorrSign(
|
function schnorrSign(message: Hex, privateKey: Hex, auxRand: Hex = randomBytes(32)): Uint8Array {
|
||||||
message: Hex,
|
|
||||||
privateKey: PrivKey,
|
|
||||||
auxRand: Hex = randomBytes(32)
|
|
||||||
): Uint8Array {
|
|
||||||
if (message == null) throw new Error(`sign: Expected valid message, not "${message}"`);
|
if (message == null) throw new Error(`sign: Expected valid message, not "${message}"`);
|
||||||
const m = ensureBytes(message);
|
const m = ensureBytes(message);
|
||||||
// checks for isWithinCurveOrder
|
// checks for isWithinCurveOrder
|
||||||
const { x: px, scalar: d } = schnorrGetScalar(normalizePrivateKey(privateKey));
|
const { x: px, scalar: d } = schnorrGetScalar(b2num(ensureBytes(privateKey, 32)));
|
||||||
const rand = ensureBytes(auxRand);
|
const a = ensureBytes(auxRand, 32); // Auxiliary random data a: a 32-byte array
|
||||||
if (rand.length !== 32) throw new Error('sign: Expected 32 bytes of aux randomness');
|
// TODO: replace with proper xor?
|
||||||
const tag = taggedHash;
|
const t = numTo32b(d ^ bytesToNumberBE(tag(TAGS.aux, a))); // Let t be the byte-wise xor of bytes(d) and hashBIP0340/aux(a)
|
||||||
const t0h = tag(TAGS.aux, rand);
|
const rand = tag(TAGS.nonce, t, px, m); // Let rand = hashBIP0340/nonce(t || bytes(P) || m)
|
||||||
const t = numTo32b(d ^ bytesToNumberBE(t0h));
|
const k_ = modN(bytesToNumberBE(rand)); // Let k' = int(rand) mod n
|
||||||
const k0h = tag(TAGS.nonce, t, px, m);
|
if (k_ === _0n) throw new Error('sign failed: k is zero'); // Fail if k' = 0.
|
||||||
const k0 = mod(bytesToNumberBE(k0h), secp256k1.CURVE.n);
|
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k_);
|
||||||
if (k0 === _0n) throw new Error('sign: Creation of signature failed. k is zero');
|
const e = modN(b2num(tag(TAGS.challenge, rx, px, m)));
|
||||||
const { point: R, x: rx, scalar: k } = schnorrGetScalar(k0);
|
const sig = packSig(R.px, modN(k + e * d));
|
||||||
const e = schnorrChallengeFinalize(tag(TAGS.challenge, rx, px, m));
|
|
||||||
const sig = new SchnorrSignature(R.px, mod(k + e * d, secp256k1.CURVE.n)).toRawBytes();
|
|
||||||
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
if (!schnorrVerify(sig, m, px)) throw new Error('sign: Invalid signature produced');
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
@ -288,23 +192,12 @@ function schnorrSign(
|
|||||||
*/
|
*/
|
||||||
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
||||||
try {
|
try {
|
||||||
const raw = signature instanceof SchnorrSignature;
|
const P = lift_x(b2num(ensureBytes(publicKey, 32))); // P = lift_x(int(pk)); fail if that fails
|
||||||
const sig: SchnorrSignature = raw ? signature : SchnorrSignature.fromHex(signature);
|
const { r, s } = unpackSig(ensureBytes(signature, 64));
|
||||||
if (raw) sig.assertValidity(); // just in case
|
|
||||||
|
|
||||||
const { r, s } = sig;
|
|
||||||
const m = ensureBytes(message);
|
const m = ensureBytes(message);
|
||||||
const P = normalizePublicKey(publicKey);
|
const e = modN(b2num(tag(TAGS.challenge, numTo32b(r), toRawX(P), m)));
|
||||||
const e = schnorrChallengeFinalize(taggedHash(TAGS.challenge, numTo32b(r), toRawX(P), m));
|
const R = PPoint.BASE.multiplyAndAddUnsafe(P, s, modN(-e)); // R = s⋅G - e⋅P
|
||||||
// Finalize
|
if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false; // -eP == (n-e)P
|
||||||
// R = s⋅G - e⋅P
|
|
||||||
// -eP == (n-e)P
|
|
||||||
const R = secp256k1.ProjectivePoint.BASE.multiplyAndAddUnsafe(
|
|
||||||
P,
|
|
||||||
normalizePrivateKey(s),
|
|
||||||
mod(-e, secp256k1.CURVE.n)
|
|
||||||
);
|
|
||||||
if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false;
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
@ -312,10 +205,63 @@ function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const schnorr = {
|
export const schnorr = {
|
||||||
Signature: SchnorrSignature,
|
|
||||||
// Schnorr's pubkey is just `x` of Point (BIP340)
|
// Schnorr's pubkey is just `x` of Point (BIP340)
|
||||||
getPublicKey: (privateKey: PrivKey): Uint8Array =>
|
getPublicKey: (privateKey: PrivKey): Uint8Array => toRawX(PPoint.fromPrivateKey(privateKey)),
|
||||||
toRawX(secp256k1.ProjectivePoint.fromPrivateKey(privateKey)),
|
|
||||||
sign: schnorrSign,
|
sign: schnorrSign,
|
||||||
verify: schnorrVerify,
|
verify: schnorrVerify,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isoMap = htf.isogenyMap(
|
||||||
|
Fp,
|
||||||
|
[
|
||||||
|
// xNum
|
||||||
|
[
|
||||||
|
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa8c7',
|
||||||
|
'0x7d3d4c80bc321d5b9f315cea7fd44c5d595d2fc0bf63b92dfff1044f17c6581',
|
||||||
|
'0x534c328d23f234e6e2a413deca25caece4506144037c40314ecbd0b53d9dd262',
|
||||||
|
'0x8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38daaaaa88c',
|
||||||
|
],
|
||||||
|
// xDen
|
||||||
|
[
|
||||||
|
'0xd35771193d94918a9ca34ccbb7b640dd86cd409542f8487d9fe6b745781eb49b',
|
||||||
|
'0xedadc6f64383dc1df7c4b2d51b54225406d36b641f5e41bbc52a56612a8c6d14',
|
||||||
|
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
||||||
|
],
|
||||||
|
// yNum
|
||||||
|
[
|
||||||
|
'0x4bda12f684bda12f684bda12f684bda12f684bda12f684bda12f684b8e38e23c',
|
||||||
|
'0xc75e0c32d5cb7c0fa9d0a54b12a0a6d5647ab046d686da6fdffc90fc201d71a3',
|
||||||
|
'0x29a6194691f91a73715209ef6512e576722830a201be2018a765e85a9ecee931',
|
||||||
|
'0x2f684bda12f684bda12f684bda12f684bda12f684bda12f684bda12f38e38d84',
|
||||||
|
],
|
||||||
|
// yDen
|
||||||
|
[
|
||||||
|
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffff93b',
|
||||||
|
'0x7a06534bb8bdb49fd5e9e6632722c2989467c1bfc8e8d978dfb425d2685c2573',
|
||||||
|
'0x6484aa716545ca2cf3a70c3fa8fe337e0a3d21162f0d6299a7bf8192bfd2a76f',
|
||||||
|
'0x0000000000000000000000000000000000000000000000000000000000000001', // LAST 1
|
||||||
|
],
|
||||||
|
].map((i) => i.map((j) => BigInt(j))) as [Fp[], Fp[], Fp[], Fp[]]
|
||||||
|
);
|
||||||
|
const mapSWU = mapToCurveSimpleSWU(Fp, {
|
||||||
|
A: BigInt('0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533'),
|
||||||
|
B: BigInt('1771'),
|
||||||
|
Z: Fp.create(BigInt('-11')),
|
||||||
|
});
|
||||||
|
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||||
|
secp256k1.ProjectivePoint,
|
||||||
|
(scalars: bigint[]) => {
|
||||||
|
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
||||||
|
return isoMap(x, y);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DST: 'secp256k1_XMD:SHA-256_SSWU_RO_',
|
||||||
|
encodeDST: 'secp256k1_XMD:SHA-256_SSWU_NU_',
|
||||||
|
p: Fp.ORDER,
|
||||||
|
m: 1,
|
||||||
|
k: 128,
|
||||||
|
expand: 'xmd',
|
||||||
|
hash: sha256,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export { hashToCurve, encodeToCurve };
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
|
||||||
import { keccak_256 } from '@noble/hashes/sha3';
|
import { keccak_256 } from '@noble/hashes/sha3';
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import { weierstrass, ProjectivePointType } from './abstract/weierstrass.js';
|
import { weierstrass, ProjPointType } from './abstract/weierstrass.js';
|
||||||
import * as cutils from './abstract/utils.js';
|
import * as cutils from './abstract/utils.js';
|
||||||
import { Fp, mod, Field, validateField } from './abstract/modular.js';
|
import { Fp, mod, Field, validateField } from './abstract/modular.js';
|
||||||
import { getHash } from './_shortw_utils.js';
|
import { getHash } from './_shortw_utils.js';
|
||||||
import * as poseidon from './abstract/poseidon.js';
|
import * as poseidon from './abstract/poseidon.js';
|
||||||
import { utf8ToBytes } from '@noble/hashes/utils';
|
import { utf8ToBytes } from '@noble/hashes/utils';
|
||||||
|
|
||||||
type ProjectivePoint = ProjectivePointType<bigint>;
|
type ProjectivePoint = ProjPointType<bigint>;
|
||||||
// Stark-friendly elliptic curve
|
// Stark-friendly elliptic curve
|
||||||
// https://docs.starkware.co/starkex/stark-curve.html
|
// https://docs.starkware.co/starkex/stark-curve.html
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user