weierstrass: remove affine Point
This commit is contained in:
parent
17e5be5f1b
commit
2ed27da8eb
@ -16,13 +16,18 @@ import * as ut from './utils.js';
|
|||||||
// Types require separate import
|
// Types require separate import
|
||||||
import { Hex, PrivKey } from './utils.js';
|
import { Hex, PrivKey } from './utils.js';
|
||||||
import * as htf from './hash-to-curve.js';
|
import * as htf from './hash-to-curve.js';
|
||||||
import { CurvePointsType, PointType, CurvePointsRes, weierstrassPoints } from './weierstrass.js';
|
import {
|
||||||
|
CurvePointsType,
|
||||||
|
ProjectivePointType as PPointType,
|
||||||
|
CurvePointsRes,
|
||||||
|
weierstrassPoints,
|
||||||
|
} from './weierstrass.js';
|
||||||
|
|
||||||
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): PointType<Fp2>;
|
decode(hex: Hex): PPointType<Fp2>;
|
||||||
encode(point: PointType<Fp2>): Uint8Array;
|
encode(point: PPointType<Fp2>): Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
export type CurveType<Fp, Fp2, Fp6, Fp12> = {
|
||||||
@ -73,29 +78,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: PointType<Fp>, Q: PointType<Fp2>, withFinalExponent?: boolean) => Fp12;
|
pairing: (P: PPointType<Fp>, Q: PPointType<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: PointType<Fp2>, privateKey: PrivKey): PointType<Fp2>;
|
(message: PPointType<Fp2>, privateKey: PrivKey): PPointType<Fp2>;
|
||||||
};
|
};
|
||||||
verify: (
|
verify: (
|
||||||
signature: Hex | PointType<Fp2>,
|
signature: Hex | PPointType<Fp2>,
|
||||||
message: Hex | PointType<Fp2>,
|
message: Hex | PPointType<Fp2>,
|
||||||
publicKey: Hex | PointType<Fp>
|
publicKey: Hex | PPointType<Fp>
|
||||||
) => boolean;
|
) => boolean;
|
||||||
aggregatePublicKeys: {
|
aggregatePublicKeys: {
|
||||||
(publicKeys: Hex[]): Uint8Array;
|
(publicKeys: Hex[]): Uint8Array;
|
||||||
(publicKeys: PointType<Fp>[]): PointType<Fp>;
|
(publicKeys: PPointType<Fp>[]): PPointType<Fp>;
|
||||||
};
|
};
|
||||||
aggregateSignatures: {
|
aggregateSignatures: {
|
||||||
(signatures: Hex[]): Uint8Array;
|
(signatures: Hex[]): Uint8Array;
|
||||||
(signatures: PointType<Fp2>[]): PointType<Fp2>;
|
(signatures: PPointType<Fp2>[]): PPointType<Fp2>;
|
||||||
};
|
};
|
||||||
verifyBatch: (
|
verifyBatch: (
|
||||||
signature: Hex | PointType<Fp2>,
|
signature: Hex | PPointType<Fp2>,
|
||||||
messages: (Hex | PointType<Fp2>)[],
|
messages: (Hex | PPointType<Fp2>)[],
|
||||||
publicKeys: (Hex | PointType<Fp>)[]
|
publicKeys: (Hex | PPointType<Fp>)[]
|
||||||
) => boolean;
|
) => boolean;
|
||||||
utils: {
|
utils: {
|
||||||
stringToBytes: typeof htf.stringToBytes;
|
stringToBytes: typeof htf.stringToBytes;
|
||||||
@ -196,7 +201,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
n: Fr.ORDER,
|
n: Fr.ORDER,
|
||||||
...CURVE.G1,
|
...CURVE.G1,
|
||||||
});
|
});
|
||||||
const G1HashToCurve = htf.hashToCurve(G1.Point, CURVE.G1.mapToCurve, {
|
const G1HashToCurve = htf.hashToCurve(G1.ProjectivePoint, CURVE.G1.mapToCurve, {
|
||||||
...CURVE.htfDefaults,
|
...CURVE.htfDefaults,
|
||||||
...CURVE.G1.htfDefaults,
|
...CURVE.G1.htfDefaults,
|
||||||
});
|
});
|
||||||
@ -226,7 +231,8 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
n: Fr.ORDER,
|
n: Fr.ORDER,
|
||||||
...CURVE.G2,
|
...CURVE.G2,
|
||||||
});
|
});
|
||||||
const G2HashToCurve = htf.hashToCurve(G2.Point, CURVE.G2.mapToCurve, {
|
const C = G2.ProjectivePoint as htf.H2CPointConstructor<Fp2>; // TODO: fix
|
||||||
|
const G2HashToCurve = htf.hashToCurve(C, CURVE.G2.mapToCurve, {
|
||||||
...CURVE.htfDefaults,
|
...CURVE.htfDefaults,
|
||||||
...CURVE.G2.htfDefaults,
|
...CURVE.G2.htfDefaults,
|
||||||
});
|
});
|
||||||
@ -235,7 +241,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
|
|
||||||
// Calculates bilinear pairing
|
// Calculates bilinear pairing
|
||||||
function pairing(P: G1, Q: G2, withFinalExponent: boolean = true): Fp12 {
|
function pairing(P: G1, Q: G2, withFinalExponent: boolean = true): Fp12 {
|
||||||
if (P.equals(G1.Point.ZERO) || Q.equals(G2.Point.ZERO))
|
if (P.equals(G1.ProjectivePoint.ZERO) || Q.equals(G2.ProjectivePoint.ZERO))
|
||||||
throw new Error('No pairings at point of Infinity');
|
throw new Error('No pairings at point of Infinity');
|
||||||
P.assertValidity();
|
P.assertValidity();
|
||||||
Q.assertValidity();
|
Q.assertValidity();
|
||||||
@ -243,25 +249,27 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
const looped = millerLoopG1(P, Q);
|
const looped = millerLoopG1(P, Q);
|
||||||
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
return withFinalExponent ? Fp12.finalExponentiate(looped) : looped;
|
||||||
}
|
}
|
||||||
type G1 = typeof G1.Point.BASE;
|
type G1 = typeof G1.ProjectivePoint.BASE;
|
||||||
type G2 = typeof G2.Point.BASE;
|
type G2 = typeof G2.ProjectivePoint.BASE;
|
||||||
|
|
||||||
type G1Hex = Hex | G1;
|
type G1Hex = Hex | G1;
|
||||||
type G2Hex = Hex | G2;
|
type G2Hex = Hex | G2;
|
||||||
function normP1(point: G1Hex): G1 {
|
function normP1(point: G1Hex): G1 {
|
||||||
return point instanceof G1.Point ? (point as G1) : G1.Point.fromHex(point);
|
return point instanceof G1.ProjectivePoint ? (point as G1) : G1.ProjectivePoint.fromHex(point);
|
||||||
}
|
}
|
||||||
function normP2(point: G2Hex): G2 {
|
function normP2(point: G2Hex): G2 {
|
||||||
return point instanceof G2.Point ? point : Signature.decode(point);
|
return point instanceof G2.ProjectivePoint ? point : Signature.decode(point);
|
||||||
}
|
}
|
||||||
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
|
function normP2Hash(point: G2Hex, htfOpts?: htf.htfBasicOpts): G2 {
|
||||||
return point instanceof G2.Point ? point : (G2HashToCurve.hashToCurve(point, htfOpts) as G2);
|
return point instanceof G2.ProjectivePoint
|
||||||
|
? point
|
||||||
|
: (G2HashToCurve.hashToCurve(point, htfOpts) as G2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiplies generator by private key.
|
// Multiplies generator by private key.
|
||||||
// P = pk x G
|
// P = pk x G
|
||||||
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
function getPublicKey(privateKey: PrivKey): Uint8Array {
|
||||||
return G1.Point.fromPrivateKey(privateKey).toRawBytes(true);
|
return G1.ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Executes `hashToCurve` on the message and then multiplies the result by private key.
|
// Executes `hashToCurve` on the message and then multiplies the result by private key.
|
||||||
@ -272,7 +280,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
const msgPoint = normP2Hash(message, htfOpts);
|
const msgPoint = normP2Hash(message, htfOpts);
|
||||||
msgPoint.assertValidity();
|
msgPoint.assertValidity();
|
||||||
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
const sigPoint = msgPoint.multiply(G1.normalizePrivateKey(privateKey));
|
||||||
if (message instanceof G2.Point) return sigPoint;
|
if (message instanceof G2.ProjectivePoint) return sigPoint;
|
||||||
return Signature.encode(sigPoint);
|
return Signature.encode(sigPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +294,7 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
): boolean {
|
): boolean {
|
||||||
const P = normP1(publicKey);
|
const P = normP1(publicKey);
|
||||||
const Hm = normP2Hash(message, htfOpts);
|
const Hm = normP2Hash(message, htfOpts);
|
||||||
const G = G1.Point.BASE;
|
const G = G1.ProjectivePoint.BASE;
|
||||||
const S = normP2(signature);
|
const S = normP2(signature);
|
||||||
// Instead of doing 2 exponentiations, we use property of billinear maps
|
// Instead of doing 2 exponentiations, we use property of billinear maps
|
||||||
// and do one exp after multiplying 2 points.
|
// and do one exp after multiplying 2 points.
|
||||||
@ -305,8 +313,8 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
const agg = publicKeys
|
const agg = publicKeys
|
||||||
.map(normP1)
|
.map(normP1)
|
||||||
.reduce((sum, p) => sum.add(G1.ProjectivePoint.fromAffine(p)), G1.ProjectivePoint.ZERO);
|
.reduce((sum, p) => sum.add(G1.ProjectivePoint.fromAffine(p)), G1.ProjectivePoint.ZERO);
|
||||||
const aggAffine = agg.toAffine();
|
const aggAffine = agg; //.toAffine();
|
||||||
if (publicKeys[0] instanceof G1.Point) {
|
if (publicKeys[0] instanceof G1.ProjectivePoint) {
|
||||||
aggAffine.assertValidity();
|
aggAffine.assertValidity();
|
||||||
return aggAffine;
|
return aggAffine;
|
||||||
}
|
}
|
||||||
@ -322,8 +330,8 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
const agg = signatures
|
const agg = signatures
|
||||||
.map(normP2)
|
.map(normP2)
|
||||||
.reduce((sum, s) => sum.add(G2.ProjectivePoint.fromAffine(s)), G2.ProjectivePoint.ZERO);
|
.reduce((sum, s) => sum.add(G2.ProjectivePoint.fromAffine(s)), G2.ProjectivePoint.ZERO);
|
||||||
const aggAffine = agg.toAffine();
|
const aggAffine = agg; //.toAffine();
|
||||||
if (signatures[0] instanceof G2.Point) {
|
if (signatures[0] instanceof G2.ProjectivePoint) {
|
||||||
aggAffine.assertValidity();
|
aggAffine.assertValidity();
|
||||||
return aggAffine;
|
return aggAffine;
|
||||||
}
|
}
|
||||||
@ -350,13 +358,13 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
const groupPublicKey = nMessages.reduce(
|
const groupPublicKey = nMessages.reduce(
|
||||||
(groupPublicKey, subMessage, i) =>
|
(groupPublicKey, subMessage, i) =>
|
||||||
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
subMessage === message ? groupPublicKey.add(nPublicKeys[i]) : groupPublicKey,
|
||||||
G1.Point.ZERO
|
G1.ProjectivePoint.ZERO
|
||||||
);
|
);
|
||||||
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
// const msg = message instanceof PointG2 ? message : await PointG2.hashToCurve(message);
|
||||||
// Possible to batch pairing for same msg with different groupPublicKey here
|
// Possible to batch pairing for same msg with different groupPublicKey here
|
||||||
paired.push(pairing(groupPublicKey, message, false));
|
paired.push(pairing(groupPublicKey, message, false));
|
||||||
}
|
}
|
||||||
paired.push(pairing(G1.Point.BASE.negate(), sig, false));
|
paired.push(pairing(G1.ProjectivePoint.BASE.negate(), sig, false));
|
||||||
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
const product = paired.reduce((a, b) => Fp12.mul(a, b), Fp12.ONE);
|
||||||
const exp = Fp12.finalExponentiate(product);
|
const exp = Fp12.finalExponentiate(product);
|
||||||
return Fp12.equals(exp, Fp12.ONE);
|
return Fp12.equals(exp, Fp12.ONE);
|
||||||
@ -366,7 +374,8 @@ export function bls<Fp2, Fp6, Fp12>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pre-compute points. Refer to README.
|
// Pre-compute points. Refer to README.
|
||||||
G1.Point.BASE._setWindowSize(4);
|
// TODO
|
||||||
|
// G1.ProjectivePoint.BASE._setWindowSize(4);
|
||||||
return {
|
return {
|
||||||
CURVE,
|
CURVE,
|
||||||
Fr,
|
Fr,
|
||||||
|
@ -93,7 +93,6 @@ export type CurveFn = {
|
|||||||
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => Uint8Array;
|
getPublicKey: (privateKey: PrivKey, isCompressed?: boolean) => 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;
|
||||||
// Point: PointConstructor;
|
|
||||||
ExtendedPoint: ExtendedPointConstructor;
|
ExtendedPoint: ExtendedPointConstructor;
|
||||||
utils: {
|
utils: {
|
||||||
randomPrivateKey: () => Uint8Array;
|
randomPrivateKey: () => Uint8Array;
|
||||||
|
@ -178,17 +178,16 @@ export function isogenyMap<T, F extends mod.Field<T>>(field: F, map: [T[], T[],
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Point<T> extends Group<Point<T>> {
|
export interface H2CPoint<T> extends Group<H2CPoint<T>> {
|
||||||
// readonly x: T;
|
readonly x: T;
|
||||||
// readonly y: T;
|
readonly y: T;
|
||||||
add(rhs: Point<T>): Point<T>;
|
add(rhs: H2CPoint<T>): H2CPoint<T>;
|
||||||
toAffine(iz?: bigint): { x: T; y: T };
|
toAffine(iz?: bigint): { x: T; y: T };
|
||||||
clearCofactor(): Point<T>;
|
clearCofactor(): H2CPoint<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PointConstructor<T> extends GroupConstructor<Point<T>> {
|
export interface H2CPointConstructor<T> extends GroupConstructor<H2CPoint<T>> {
|
||||||
// new (x: T, y: T): Point<T>;
|
fromAffine(ap: { x: T; y: T }): H2CPoint<T>;
|
||||||
fromAffine(ap: { x: T; y: T }): Point<T>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MapToCurve<T> = (scalar: bigint[]) => { x: T; y: T };
|
export type MapToCurve<T> = (scalar: bigint[]) => { x: T; y: T };
|
||||||
@ -198,7 +197,11 @@ export type htfBasicOpts = {
|
|||||||
DST: string;
|
DST: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function hashToCurve<T>(Point: PointConstructor<T>, mapToCurve: MapToCurve<T>, def: Opts) {
|
export function hashToCurve<T>(
|
||||||
|
Point: H2CPointConstructor<T>,
|
||||||
|
mapToCurve: MapToCurve<T>,
|
||||||
|
def: Opts
|
||||||
|
) {
|
||||||
validateOpts(def);
|
validateOpts(def);
|
||||||
if (typeof mapToCurve !== 'function')
|
if (typeof mapToCurve !== 'function')
|
||||||
throw new Error('hashToCurve: mapToCurve() has not been defined');
|
throw new Error('hashToCurve: mapToCurve() has not been defined');
|
||||||
|
@ -109,48 +109,50 @@ export type SignOpts = { lowS?: boolean; extraEntropy?: Entropy };
|
|||||||
* TODO: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#unique-symbol
|
* TODO: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#unique-symbol
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export interface AffinePoint<T> {
|
||||||
|
x: T;
|
||||||
|
y: T;
|
||||||
|
}
|
||||||
// Instance for 3d XYZ points
|
// Instance for 3d XYZ points
|
||||||
export interface ProjectivePointType<T> extends Group<ProjectivePointType<T>> {
|
export interface ProjectivePointType<T> extends Group<ProjectivePointType<T>> {
|
||||||
readonly x: T;
|
readonly x: T;
|
||||||
readonly y: T;
|
readonly y: T;
|
||||||
readonly z: T;
|
readonly z: T;
|
||||||
multiply(scalar: bigint, affinePoint?: PointType<T>): ProjectivePointType<T>;
|
multiply(scalar: bigint): ProjectivePointType<T>;
|
||||||
multiplyUnsafe(scalar: bigint): ProjectivePointType<T>;
|
multiplyUnsafe(scalar: bigint): ProjectivePointType<T>;
|
||||||
toAffine(invZ?: T): PointType<T>;
|
multiplyAndAddUnsafe(
|
||||||
|
Q: ProjectivePointType<T>,
|
||||||
|
a: bigint,
|
||||||
|
b: bigint
|
||||||
|
): ProjectivePointType<T> | undefined;
|
||||||
|
toAffine(invZ?: T): AffinePoint<T>;
|
||||||
|
isTorsionFree(): boolean;
|
||||||
clearCofactor(): ProjectivePointType<T>;
|
clearCofactor(): ProjectivePointType<T>;
|
||||||
}
|
assertValidity(): void;
|
||||||
// Static methods for 3d XYZ points
|
|
||||||
export interface ProjectiveConstructor<T> extends GroupConstructor<ProjectivePointType<T>> {
|
|
||||||
new (x: T, y: T, z: T): ProjectivePointType<T>;
|
|
||||||
fromAffine(p: PointType<T>): ProjectivePointType<T>;
|
|
||||||
toAffineBatch(points: ProjectivePointType<T>[]): PointType<T>[];
|
|
||||||
normalizeZ(points: ProjectivePointType<T>[]): ProjectivePointType<T>[];
|
|
||||||
}
|
|
||||||
// Instance for 2d XY points
|
|
||||||
export interface PointType<T> extends Group<PointType<T>> {
|
|
||||||
readonly x: T;
|
|
||||||
readonly y: T;
|
|
||||||
_setWindowSize(windowSize: number): void;
|
|
||||||
hasEvenY(): boolean;
|
hasEvenY(): boolean;
|
||||||
toRawBytes(isCompressed?: boolean): Uint8Array;
|
toRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
toHex(isCompressed?: boolean): string;
|
toHex(isCompressed?: boolean): string;
|
||||||
assertValidity(): void;
|
|
||||||
multiplyAndAddUnsafe(Q: PointType<T>, a: bigint, b: bigint): PointType<T> | undefined;
|
|
||||||
clearCofactor(): PointType<T>;
|
|
||||||
toAffine(iz?: bigint): { x: T; y: T };
|
|
||||||
}
|
}
|
||||||
// Static methods for 2d XY points
|
// Static methods for 3d XYZ points
|
||||||
export interface PointConstructor<T> extends GroupConstructor<PointType<T>> {
|
export interface ProjectiveConstructor<T> extends GroupConstructor<ProjectivePointType<T>> {
|
||||||
new (x: T, y: T): PointType<T>;
|
new (x: T, y: T, z?: T): ProjectivePointType<T>;
|
||||||
fromAffine(ap: { x: T; y: T }): PointType<T>;
|
fromAffine(p: AffinePoint<T>): ProjectivePointType<T>;
|
||||||
fromHex(hex: Hex): PointType<T>;
|
toAffineBatch(points: ProjectivePointType<T>[]): AffinePoint<T>[];
|
||||||
fromPrivateKey(privateKey: PrivKey): PointType<T>;
|
normalizeZ(points: ProjectivePointType<T>[]): ProjectivePointType<T>[];
|
||||||
|
|
||||||
|
fromAffine(ap: { x: T; y: T }): ProjectivePointType<T>;
|
||||||
|
fromHex(hex: Hex): ProjectivePointType<T>;
|
||||||
|
fromPrivateKey(privateKey: PrivKey): ProjectivePointType<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CurvePointsType<T> = BasicCurve<T> & {
|
export type CurvePointsType<T> = BasicCurve<T> & {
|
||||||
// Bytes
|
// Bytes
|
||||||
fromBytes: (bytes: Uint8Array) => { x: T; y: T };
|
fromBytes: (bytes: Uint8Array) => { x: T; y: T };
|
||||||
toBytes: (c: PointConstructor<T>, point: PointType<T>, compressed: boolean) => Uint8Array;
|
toBytes: (
|
||||||
|
c: ProjectiveConstructor<T>,
|
||||||
|
point: ProjectivePointType<T>,
|
||||||
|
compressed: boolean
|
||||||
|
) => Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
||||||
@ -184,7 +186,7 @@ function validatePointOpts<T>(curve: CurvePointsType<T>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type CurvePointsRes<T> = {
|
export type CurvePointsRes<T> = {
|
||||||
Point: PointConstructor<T>;
|
// Point: PointConstructor<T>;
|
||||||
ProjectivePoint: ProjectiveConstructor<T>;
|
ProjectivePoint: ProjectiveConstructor<T>;
|
||||||
normalizePrivateKey: (key: PrivKey) => bigint;
|
normalizePrivateKey: (key: PrivKey) => bigint;
|
||||||
weierstrassEquation: (x: T) => T;
|
weierstrassEquation: (x: T) => T;
|
||||||
@ -194,7 +196,6 @@ export type CurvePointsRes<T> = {
|
|||||||
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
// Be friendly to bad ECMAScript parsers by not using bigint literals like 123n
|
||||||
const _0n = BigInt(0);
|
const _0n = BigInt(0);
|
||||||
const _1n = BigInt(1);
|
const _1n = BigInt(1);
|
||||||
const _3n = BigInt(3);
|
|
||||||
|
|
||||||
export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
||||||
const CURVE = validatePointOpts(opts);
|
const CURVE = validatePointOpts(opts);
|
||||||
@ -263,17 +264,19 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
* 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 ProjectivePointType<T> {
|
||||||
constructor(readonly x: T, readonly y: T, readonly z: T) {}
|
constructor(readonly x: T, readonly y: T, readonly z: T = Fp.ONE) {}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
static fromAffine(p: Point): ProjectivePoint {
|
static fromAffine(p: AffinePoint<T>): ProjectivePoint {
|
||||||
if (!(p instanceof Point)) {
|
// TODO: validate
|
||||||
throw new TypeError('ProjectivePoint#fromAffine: expected Point');
|
// if (!(p instanceof Point)) {
|
||||||
}
|
// throw new TypeError('ProjectivePoint#fromAffine: expected Point');
|
||||||
|
// }
|
||||||
// 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 (p.equals(Point.ZERO)) return ProjectivePoint.ZERO;
|
// if (p.equals(Point.ZERO)) return ProjectivePoint.ZERO;
|
||||||
|
if (Fp.equals(p.x, Fp.ZERO) && Fp.equals(p.y, Fp.ZERO)) return ProjectivePoint.ZERO;
|
||||||
return new ProjectivePoint(p.x, p.y, Fp.ONE);
|
return new ProjectivePoint(p.x, p.y, Fp.ONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +285,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
* inversion on all of them. Inversion is very slow operation,
|
* inversion on all of them. Inversion is very slow operation,
|
||||||
* so this improves performance massively.
|
* so this improves performance massively.
|
||||||
*/
|
*/
|
||||||
static toAffineBatch(points: ProjectivePoint[]): Point[] {
|
static toAffineBatch(points: ProjectivePoint[]): AffinePoint<T>[] {
|
||||||
const toInv = Fp.invertBatch(points.map((p) => p.z));
|
const toInv = Fp.invertBatch(points.map((p) => p.z));
|
||||||
return points.map((p, i) => p.toAffine(toInv[i]));
|
return points.map((p, i) => p.toAffine(toInv[i]));
|
||||||
}
|
}
|
||||||
@ -445,25 +448,6 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
k2p = new ProjectivePoint(Fp.mul(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
|
k2p = new ProjectivePoint(Fp.mul(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
|
||||||
return k1p.add(k2p);
|
return k1p.add(k2p);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements w-ary non-adjacent form for calculating ec multiplication.
|
|
||||||
*/
|
|
||||||
private wNAF(n: bigint, affinePoint?: Point): { p: ProjectivePoint; f: ProjectivePoint } {
|
|
||||||
if (!affinePoint && this.equals(ProjectivePoint.BASE)) affinePoint = Point.BASE;
|
|
||||||
const W = (affinePoint && affinePoint._WINDOW_SIZE) || 1;
|
|
||||||
// Calculate precomputes on a first run, reuse them after
|
|
||||||
let precomputes = affinePoint && pointPrecomputes.get(affinePoint);
|
|
||||||
if (!precomputes) {
|
|
||||||
precomputes = wnaf.precomputeWindow(this, W) as ProjectivePoint[];
|
|
||||||
if (affinePoint && W !== 1) {
|
|
||||||
precomputes = ProjectivePoint.normalizeZ(precomputes);
|
|
||||||
pointPrecomputes.set(affinePoint, precomputes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wnaf.wNAF(W, precomputes, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant time multiplication.
|
* Constant time multiplication.
|
||||||
* Uses wNAF method. Windowed method may be 10% faster,
|
* Uses wNAF method. Windowed method may be 10% faster,
|
||||||
@ -472,7 +456,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
* @param affinePoint optional point ot save cached precompute windows on it
|
* @param affinePoint optional point ot save cached precompute windows on it
|
||||||
* @returns New point
|
* @returns New point
|
||||||
*/
|
*/
|
||||||
multiply(scalar: bigint, affinePoint?: Point): ProjectivePoint {
|
multiply(scalar: bigint): ProjectivePoint {
|
||||||
let n = normalizeScalar(scalar);
|
let n = normalizeScalar(scalar);
|
||||||
|
|
||||||
// Real point.
|
// Real point.
|
||||||
@ -481,15 +465,15 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
let fake: ProjectivePoint;
|
let fake: ProjectivePoint;
|
||||||
if (CURVE.endo) {
|
if (CURVE.endo) {
|
||||||
const { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n);
|
const { k1neg, k1, k2neg, k2 } = CURVE.endo.splitScalar(n);
|
||||||
let { p: k1p, f: f1p } = this.wNAF(k1, affinePoint);
|
let { p: k1p, f: f1p } = wNAF_TMP_FN(this, k1);
|
||||||
let { p: k2p, f: f2p } = this.wNAF(k2, affinePoint);
|
let { p: k2p, f: f2p } = wNAF_TMP_FN(this, k2);
|
||||||
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
k1p = wnaf.constTimeNegate(k1neg, k1p);
|
||||||
k2p = wnaf.constTimeNegate(k2neg, k2p);
|
k2p = wnaf.constTimeNegate(k2neg, k2p);
|
||||||
k2p = new ProjectivePoint(Fp.mul(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
|
k2p = new ProjectivePoint(Fp.mul(k2p.x, CURVE.endo.beta), k2p.y, k2p.z);
|
||||||
point = k1p.add(k2p);
|
point = k1p.add(k2p);
|
||||||
fake = f1p.add(f2p);
|
fake = f1p.add(f2p);
|
||||||
} else {
|
} else {
|
||||||
const { p, f } = this.wNAF(n, affinePoint);
|
const { p, f } = wNAF_TMP_FN(this, n);
|
||||||
point = p;
|
point = p;
|
||||||
fake = f;
|
fake = f;
|
||||||
}
|
}
|
||||||
@ -500,7 +484,7 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
// Converts Projective point to affine (x, y) coordinates.
|
// Converts Projective point to affine (x, y) coordinates.
|
||||||
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
// Can accept precomputed Z^-1 - for example, from invertBatch.
|
||||||
// (x, y, z) ∋ (x=x/z, y=y/z)
|
// (x, y, z) ∋ (x=x/z, y=y/z)
|
||||||
toAffine(invZ?: T): Point {
|
toAffine(invZ?: T): AffinePoint<T> {
|
||||||
const { x, y, z } = this;
|
const { x, y, z } = this;
|
||||||
const is0 = this.equals(ProjectivePoint.ZERO);
|
const is0 = this.equals(ProjectivePoint.ZERO);
|
||||||
// If invZ was 0, we return zero point. However we still want to execute
|
// If invZ was 0, we return zero point. However we still want to execute
|
||||||
@ -509,9 +493,9 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
const ax = Fp.mul(x, invZ);
|
const ax = Fp.mul(x, invZ);
|
||||||
const ay = Fp.mul(y, invZ);
|
const ay = Fp.mul(y, invZ);
|
||||||
const zz = Fp.mul(z, invZ);
|
const zz = Fp.mul(z, invZ);
|
||||||
if (is0) return Point.ZERO;
|
if (is0) return { x: Fp.ZERO, y: Fp.ZERO };
|
||||||
if (!Fp.equals(zz, Fp.ONE)) throw new Error('invZ was invalid');
|
if (!Fp.equals(zz, Fp.ONE)) throw new Error('invZ was invalid');
|
||||||
return new Point(ax, ay);
|
return { x: ax, y: ay };
|
||||||
}
|
}
|
||||||
isTorsionFree(): boolean {
|
isTorsionFree(): boolean {
|
||||||
const { h: cofactor, isTorsionFree } = CURVE;
|
const { h: cofactor, isTorsionFree } = CURVE;
|
||||||
@ -525,89 +509,33 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
if (clearCofactor) return clearCofactor(ProjectivePoint, this) as ProjectivePoint;
|
if (clearCofactor) return clearCofactor(ProjectivePoint, this) as ProjectivePoint;
|
||||||
return this.multiplyUnsafe(CURVE.h);
|
return this.multiplyUnsafe(CURVE.h);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const _bits = CURVE.nBitLength;
|
|
||||||
const wnaf = wNAF(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
|
||||||
|
|
||||||
function assertPrjPoint(other: unknown) {
|
|
||||||
if (!(other instanceof ProjectivePoint)) throw new TypeError('ProjectivePoint expected');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stores precomputed values for points.
|
|
||||||
const pointPrecomputes = new WeakMap<Point, ProjectivePoint[]>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default Point works in default aka affine coordinates: (x, y)
|
|
||||||
*/
|
|
||||||
class Point implements PointType<T> {
|
|
||||||
/**
|
/**
|
||||||
* Base point aka generator. Any public_key = Point.BASE * private_key
|
* Efficiently calculate `aP + bQ`.
|
||||||
|
* Unsafe, can expose private key, if used incorrectly.
|
||||||
|
* TODO: Utilize Shamir's trick
|
||||||
|
* @returns non-zero affine point
|
||||||
*/
|
*/
|
||||||
static BASE: Point = new Point(CURVE.Gx, CURVE.Gy);
|
multiplyAndAddUnsafe(Q: ProjectivePoint, a: bigint, b: bigint): ProjectivePoint | undefined {
|
||||||
/**
|
const P = this;
|
||||||
* Identity point aka point at infinity. p - p = zero_p; p + zero_p = p
|
const aP =
|
||||||
*/
|
a === _0n || a === _1n || !P.equals(ProjectivePoint.BASE)
|
||||||
static ZERO: Point = new Point(Fp.ZERO, Fp.ZERO);
|
? P.multiplyUnsafe(a)
|
||||||
static fromAffine(ap: { x: T; y: T }) {
|
: P.multiply(a);
|
||||||
return new Point(ap.x, ap.y);
|
const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b);
|
||||||
}
|
const sum = aP.add(bQ);
|
||||||
toAffine(iz?: bigint) {
|
return sum.equals(ProjectivePoint.ZERO) ? undefined : sum;
|
||||||
return { x: this.x, y: this.y };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We calculate precomputes for elliptic curve point multiplication
|
|
||||||
// using windowed method. This specifies window size and
|
|
||||||
// stores precomputed values. Usually only base point would be precomputed.
|
|
||||||
_WINDOW_SIZE?: number;
|
|
||||||
|
|
||||||
constructor(readonly x: T, readonly y: T) {}
|
|
||||||
|
|
||||||
// "Private method", don't use it directly
|
|
||||||
_setWindowSize(windowSize: number) {
|
|
||||||
this._WINDOW_SIZE = windowSize;
|
|
||||||
pointPrecomputes.delete(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks for y % 2 == 0
|
|
||||||
hasEvenY(): boolean {
|
|
||||||
if (Fp.isOdd) return !Fp.isOdd(this.y);
|
|
||||||
throw new Error("Field doesn't support isOdd");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts hash string or Uint8Array to Point.
|
|
||||||
* @param hex short/long ECDSA hex
|
|
||||||
*/
|
|
||||||
static fromHex(hex: Hex): Point {
|
|
||||||
const { x, y } = CURVE.fromBytes(ut.ensureBytes(hex));
|
|
||||||
const point = new Point(x, y);
|
|
||||||
point.assertValidity();
|
|
||||||
return point;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiplies generator point by privateKey.
|
|
||||||
static fromPrivateKey(privateKey: PrivKey) {
|
|
||||||
return Point.BASE.multiply(normalizePrivateKey(privateKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
toRawBytes(isCompressed = true): Uint8Array {
|
|
||||||
this.assertValidity();
|
|
||||||
return CURVE.toBytes(Point, this, isCompressed);
|
|
||||||
}
|
|
||||||
|
|
||||||
toHex(isCompressed = true): string {
|
|
||||||
return bytesToHex(this.toRawBytes(isCompressed));
|
|
||||||
}
|
|
||||||
// A point on curve is valid if it conforms to equation.
|
// A point on curve is valid if it conforms to equation.
|
||||||
assertValidity(): void {
|
assertValidity(): void {
|
||||||
// Zero is valid point too!
|
// Zero is valid point too!
|
||||||
if (this.equals(Point.ZERO)) {
|
if (this.equals(ProjectivePoint.ZERO)) {
|
||||||
if (CURVE.allowInfinityPoint) return;
|
if (CURVE.allowInfinityPoint) return;
|
||||||
throw new Error('Point at infinity');
|
throw new Error('Point at infinity');
|
||||||
}
|
}
|
||||||
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
// Some 3rd-party test vectors require different wording between here & `fromCompressedHex`
|
||||||
const msg = 'Point is not on elliptic curve';
|
const msg = 'Point is not on elliptic curve';
|
||||||
const { x, y } = this;
|
const { x, y } = this.toAffine();
|
||||||
// Check if x, y are valid field elements
|
// Check if x, y are valid field elements
|
||||||
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error(msg);
|
if (!Fp.isValid(x) || !Fp.isValid(y)) throw new Error(msg);
|
||||||
const left = Fp.square(y); // y²
|
const left = Fp.square(y); // y²
|
||||||
@ -615,68 +543,64 @@ export function weierstrassPoints<T>(opts: CurvePointsType<T>) {
|
|||||||
if (!Fp.equals(left, right)) throw new Error(msg);
|
if (!Fp.equals(left, right)) throw new Error(msg);
|
||||||
if (!this.isTorsionFree()) throw new Error('Point must be of prime-order subgroup');
|
if (!this.isTorsionFree()) throw new Error('Point must be of prime-order subgroup');
|
||||||
}
|
}
|
||||||
|
hasEvenY(): boolean {
|
||||||
equals(other: Point): boolean {
|
const { y } = this.toAffine();
|
||||||
if (!(other instanceof Point)) throw new TypeError('Point#equals: expected Point');
|
if (Fp.isOdd) return !Fp.isOdd(y);
|
||||||
return Fp.equals(this.x, other.x) && Fp.equals(this.y, other.y);
|
throw new Error("Field doesn't support isOdd");
|
||||||
|
}
|
||||||
|
toRawBytes(isCompressed = true): Uint8Array {
|
||||||
|
this.assertValidity();
|
||||||
|
return CURVE.toBytes(ProjectivePoint, this, isCompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the same point with inverted `y`
|
toHex(isCompressed = true): string {
|
||||||
negate() {
|
return bytesToHex(this.toRawBytes(isCompressed));
|
||||||
return new Point(this.x, Fp.negate(this.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected toProj() {
|
|
||||||
return ProjectivePoint.fromAffine(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds point to itself
|
|
||||||
double() {
|
|
||||||
return this.toProj().double().toAffine();
|
|
||||||
}
|
|
||||||
|
|
||||||
add(other: Point) {
|
|
||||||
return this.toProj().add(ProjectivePoint.fromAffine(other)).toAffine();
|
|
||||||
}
|
|
||||||
|
|
||||||
subtract(other: Point) {
|
|
||||||
return this.add(other.negate());
|
|
||||||
}
|
|
||||||
|
|
||||||
multiply(scalar: bigint) {
|
|
||||||
return this.toProj().multiply(scalar, this).toAffine();
|
|
||||||
}
|
|
||||||
|
|
||||||
multiplyUnsafe(scalar: bigint) {
|
|
||||||
return this.toProj().multiplyUnsafe(scalar).toAffine();
|
|
||||||
}
|
|
||||||
|
|
||||||
clearCofactor() {
|
|
||||||
return this.toProj().clearCofactor().toAffine();
|
|
||||||
}
|
|
||||||
|
|
||||||
isTorsionFree(): boolean {
|
|
||||||
return this.toProj().isTorsionFree();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Efficiently calculate `aP + bQ`.
|
* Converts hash string or Uint8Array to Point.
|
||||||
* Unsafe, can expose private key, if used incorrectly.
|
* @param hex short/long ECDSA hex
|
||||||
* TODO: Utilize Shamir's trick
|
|
||||||
* @returns non-zero affine point
|
|
||||||
*/
|
*/
|
||||||
multiplyAndAddUnsafe(Q: Point, a: bigint, b: bigint): Point | undefined {
|
static fromHex(hex: Hex): ProjectivePoint {
|
||||||
const P = this.toProj();
|
const { x, y } = CURVE.fromBytes(ut.ensureBytes(hex));
|
||||||
const aP =
|
const point = new ProjectivePoint(x, y);
|
||||||
a === _0n || a === _1n || this !== Point.BASE ? P.multiplyUnsafe(a) : P.multiply(a);
|
point.assertValidity();
|
||||||
const bQ = ProjectivePoint.fromAffine(Q).multiplyUnsafe(b);
|
return point;
|
||||||
const sum = aP.add(bQ);
|
}
|
||||||
return sum.equals(ProjectivePoint.ZERO) ? undefined : sum.toAffine();
|
|
||||||
|
// Multiplies generator point by privateKey.
|
||||||
|
static fromPrivateKey(privateKey: PrivKey) {
|
||||||
|
return ProjectivePoint.BASE.multiply(normalizePrivateKey(privateKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const _bits = CURVE.nBitLength;
|
||||||
|
const wnaf = wNAF(ProjectivePoint, CURVE.endo ? Math.ceil(_bits / 2) : _bits);
|
||||||
|
|
||||||
|
const { BASE: G } = ProjectivePoint;
|
||||||
|
let Gpows: ProjectivePoint[] | undefined = undefined; // precomputes for base point G
|
||||||
|
function wNAF_TMP_FN(P: ProjectivePoint, n: bigint): { p: ProjectivePoint; f: ProjectivePoint } {
|
||||||
|
const C = ProjectivePoint;
|
||||||
|
if (P.equals(G)) {
|
||||||
|
const W = 8;
|
||||||
|
if (!Gpows) {
|
||||||
|
const denorm = wnaf.precomputeWindow(P, W) as ProjectivePoint[];
|
||||||
|
const norm = C.toAffineBatch(denorm).map(C.fromAffine);
|
||||||
|
Gpows = norm;
|
||||||
|
}
|
||||||
|
const comp = Gpows;
|
||||||
|
return wnaf.wNAF(W, comp, n);
|
||||||
|
}
|
||||||
|
const W = 1;
|
||||||
|
const denorm = wnaf.precomputeWindow(P, W) as ProjectivePoint[];
|
||||||
|
// const norm = C.toAffineBatch(denorm).map(C.fromAffine);
|
||||||
|
const norm = denorm;
|
||||||
|
return wnaf.wNAF(W, norm, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertPrjPoint(other: unknown) {
|
||||||
|
if (!(other instanceof ProjectivePoint)) throw new TypeError('ProjectivePoint expected');
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
Point: Point as PointConstructor<T>,
|
|
||||||
ProjectivePoint: ProjectivePoint as ProjectiveConstructor<T>,
|
ProjectivePoint: ProjectivePoint as ProjectiveConstructor<T>,
|
||||||
normalizePrivateKey,
|
normalizePrivateKey,
|
||||||
weierstrassEquation,
|
weierstrassEquation,
|
||||||
@ -693,7 +617,7 @@ export interface SignatureType {
|
|||||||
copyWithRecoveryBit(recovery: number): SignatureType;
|
copyWithRecoveryBit(recovery: number): SignatureType;
|
||||||
hasHighS(): boolean;
|
hasHighS(): boolean;
|
||||||
normalizeS(): SignatureType;
|
normalizeS(): SignatureType;
|
||||||
recoverPublicKey(msgHash: Hex): PointType<bigint>;
|
recoverPublicKey(msgHash: Hex): ProjectivePointType<bigint>;
|
||||||
// DER-encoded
|
// DER-encoded
|
||||||
toDERRawBytes(isCompressed?: boolean): Uint8Array;
|
toDERRawBytes(isCompressed?: boolean): Uint8Array;
|
||||||
toDERHex(isCompressed?: boolean): string;
|
toDERHex(isCompressed?: boolean): string;
|
||||||
@ -707,7 +631,7 @@ export type SignatureConstructor = {
|
|||||||
fromDER(hex: Hex): SignatureType;
|
fromDER(hex: Hex): SignatureType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PubKey = Hex | PointType<bigint>;
|
export type PubKey = Hex | ProjectivePointType<bigint>;
|
||||||
|
|
||||||
export type CurveType = BasicCurve<bigint> & {
|
export type CurveType = BasicCurve<bigint> & {
|
||||||
// Default options
|
// Default options
|
||||||
@ -745,14 +669,13 @@ export type CurveFn = {
|
|||||||
lowS?: boolean;
|
lowS?: boolean;
|
||||||
}
|
}
|
||||||
) => boolean;
|
) => boolean;
|
||||||
Point: PointConstructor<bigint>;
|
|
||||||
ProjectivePoint: ProjectiveConstructor<bigint>;
|
ProjectivePoint: ProjectiveConstructor<bigint>;
|
||||||
Signature: SignatureConstructor;
|
Signature: SignatureConstructor;
|
||||||
utils: {
|
utils: {
|
||||||
_bigintToBytes: (num: bigint) => Uint8Array;
|
_bigintToBytes: (num: bigint) => Uint8Array;
|
||||||
_bigintToString: (num: bigint) => string;
|
_bigintToString: (num: bigint) => string;
|
||||||
_normalizePrivateKey: (key: PrivKey) => bigint;
|
_normalizePrivateKey: (key: PrivKey) => bigint;
|
||||||
_normalizePublicKey: (publicKey: PubKey) => PointType<bigint>;
|
_normalizePublicKey: (publicKey: PubKey) => ProjectivePointType<bigint>;
|
||||||
_isWithinCurveOrder: (num: bigint) => boolean;
|
_isWithinCurveOrder: (num: bigint) => boolean;
|
||||||
_isValidFieldElement: (num: bigint) => boolean;
|
_isValidFieldElement: (num: bigint) => boolean;
|
||||||
_weierstrassEquation: (x: bigint) => bigint;
|
_weierstrassEquation: (x: bigint) => bigint;
|
||||||
@ -827,16 +750,18 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
return _0n < num && num < Fp.ORDER;
|
return _0n < num && num < Fp.ORDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { Point, ProjectivePoint, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder } =
|
const { ProjectivePoint, normalizePrivateKey, weierstrassEquation, isWithinCurveOrder } =
|
||||||
weierstrassPoints({
|
weierstrassPoints({
|
||||||
...CURVE,
|
...CURVE,
|
||||||
toBytes(c, point, isCompressed: boolean): Uint8Array {
|
toBytes(c, point, isCompressed: boolean): Uint8Array {
|
||||||
const x = Fp.toBytes(point.x);
|
const a = point.toAffine();
|
||||||
|
const x = Fp.toBytes(a.x);
|
||||||
const cat = ut.concatBytes;
|
const cat = ut.concatBytes;
|
||||||
if (isCompressed) {
|
if (isCompressed) {
|
||||||
|
// TODO: hasEvenY
|
||||||
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
return cat(Uint8Array.from([point.hasEvenY() ? 0x02 : 0x03]), x);
|
||||||
} else {
|
} else {
|
||||||
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(point.y));
|
return cat(Uint8Array.from([0x04]), x, Fp.toBytes(a.y));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fromBytes(bytes: Uint8Array) {
|
fromBytes(bytes: Uint8Array) {
|
||||||
@ -864,7 +789,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
type Point = typeof Point.BASE;
|
// type Point = typeof ProjectivePoint.BASE;
|
||||||
|
|
||||||
// Do we need these functions at all?
|
// Do we need these functions at all?
|
||||||
function numToField(num: bigint): Uint8Array {
|
function numToField(num: bigint): Uint8Array {
|
||||||
@ -877,12 +802,12 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
/**
|
/**
|
||||||
* Normalizes hex, bytes, Point to Point. Checks for curve equation.
|
* Normalizes hex, bytes, Point to Point. Checks for curve equation.
|
||||||
*/
|
*/
|
||||||
function normalizePublicKey(publicKey: PubKey): PointType<bigint> {
|
function normalizePublicKey(publicKey: PubKey): ProjectivePointType<bigint> {
|
||||||
if (publicKey instanceof Point) {
|
if (publicKey instanceof ProjectivePoint) {
|
||||||
publicKey.assertValidity();
|
publicKey.assertValidity();
|
||||||
return publicKey;
|
return publicKey;
|
||||||
} else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
} else if (publicKey instanceof Uint8Array || typeof publicKey === 'string') {
|
||||||
return Point.fromHex(publicKey);
|
return ProjectivePoint.fromHex(publicKey);
|
||||||
// This can happen because PointType can be instance of different class
|
// This can happen because PointType can be instance of different class
|
||||||
} else throw new Error(`Unknown type of public key: ${publicKey}`);
|
} else throw new Error(`Unknown type of public key: ${publicKey}`);
|
||||||
}
|
}
|
||||||
@ -952,7 +877,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
* @param msgHash message hash
|
* @param msgHash message hash
|
||||||
* @returns Point corresponding to public key
|
* @returns Point corresponding to public key
|
||||||
*/
|
*/
|
||||||
recoverPublicKey(msgHash: Hex): Point {
|
recoverPublicKey(msgHash: Hex): typeof ProjectivePoint.BASE {
|
||||||
const { r, s, recovery } = this;
|
const { r, s, recovery } = this;
|
||||||
if (recovery == null) throw new Error('Cannot recover: recovery bit is not present');
|
if (recovery == null) throw new Error('Cannot recover: recovery bit is not present');
|
||||||
if (![0, 1, 2, 3].includes(recovery)) throw new Error('Cannot recover: invalid recovery bit');
|
if (![0, 1, 2, 3].includes(recovery)) throw new Error('Cannot recover: invalid recovery bit');
|
||||||
@ -966,8 +891,8 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const u1 = mod.mod(-h * rinv, n);
|
const u1 = mod.mod(-h * rinv, n);
|
||||||
const u2 = mod.mod(s * rinv, n);
|
const u2 = mod.mod(s * rinv, n);
|
||||||
const prefix = recovery & 1 ? '03' : '02';
|
const prefix = recovery & 1 ? '03' : '02';
|
||||||
const R = Point.fromHex(prefix + numToFieldStr(radj));
|
const R = ProjectivePoint.fromHex(prefix + numToFieldStr(radj));
|
||||||
const Q = Point.BASE.multiplyAndAddUnsafe(R, u1, u2); // unsafe is fine: no priv data leaked
|
const Q = ProjectivePoint.BASE.multiplyAndAddUnsafe(R, u1, u2); // unsafe is fine: no priv data leaked
|
||||||
if (!Q) throw new Error('Cannot recover: point at infinify');
|
if (!Q) throw new Error('Cannot recover: point at infinify');
|
||||||
Q.assertValidity();
|
Q.assertValidity();
|
||||||
return Q;
|
return Q;
|
||||||
@ -1050,11 +975,13 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
* @param windowSize 2, 4, 8, 16
|
* @param windowSize 2, 4, 8, 16
|
||||||
* @returns cached point
|
* @returns cached point
|
||||||
*/
|
*/
|
||||||
precompute(windowSize = 8, point = Point.BASE): Point {
|
precompute(windowSize = 8, point = ProjectivePoint.BASE): typeof ProjectivePoint.BASE {
|
||||||
const cached = point === Point.BASE ? point : new Point(point.x, point.y);
|
return ProjectivePoint.BASE;
|
||||||
cached._setWindowSize(windowSize);
|
// return cache
|
||||||
cached.multiply(_3n);
|
// const cached = point === Point.BASE ? point : new Point(point.x, point.y);
|
||||||
return cached;
|
// cached._setWindowSize(windowSize);
|
||||||
|
// cached.multiply(_3n);
|
||||||
|
// return cached;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1065,7 +992,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
* @returns Public key, full when isCompressed=false; short when isCompressed=true
|
||||||
*/
|
*/
|
||||||
function getPublicKey(privateKey: PrivKey, isCompressed = true): Uint8Array {
|
function getPublicKey(privateKey: PrivKey, isCompressed = true): Uint8Array {
|
||||||
return Point.fromPrivateKey(privateKey).toRawBytes(isCompressed);
|
return ProjectivePoint.fromPrivateKey(privateKey).toRawBytes(isCompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1077,7 +1004,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
const len = (arr || str) && (item as Hex).length;
|
const len = (arr || str) && (item as Hex).length;
|
||||||
if (arr) return len === compressedLen || len === uncompressedLen;
|
if (arr) return len === compressedLen || len === uncompressedLen;
|
||||||
if (str) return len === 2 * compressedLen || len === 2 * uncompressedLen;
|
if (str) return len === 2 * compressedLen || len === 2 * uncompressedLen;
|
||||||
if (item instanceof Point) return true;
|
if (item instanceof ProjectivePoint) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1170,7 +1097,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
if (!isWithinCurveOrder(k)) return;
|
if (!isWithinCurveOrder(k)) return;
|
||||||
// Important: all mod() calls in the function must be done over `n`
|
// Important: all mod() calls in the function must be done over `n`
|
||||||
const ik = mod.invert(k, n);
|
const ik = mod.invert(k, n);
|
||||||
const q = Point.BASE.multiply(k);
|
const q = ProjectivePoint.BASE.multiply(k).toAffine();
|
||||||
// r = x mod n
|
// r = x mod n
|
||||||
const r = mod.mod(q.x, n);
|
const r = mod.mod(q.x, n);
|
||||||
if (r === _0n) return;
|
if (r === _0n) return;
|
||||||
@ -1215,7 +1142,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
// Enable precomputes. Slows down first publicKey computation by 20ms.
|
||||||
Point.BASE._setWindowSize(8);
|
// Point.BASE._setWindowSize(8);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies a signature against message hash and public key.
|
* Verifies a signature against message hash and public key.
|
||||||
@ -1236,24 +1163,26 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
publicKey: PubKey,
|
publicKey: PubKey,
|
||||||
opts: { lowS?: boolean } = { lowS: CURVE.lowS }
|
opts: { lowS?: boolean } = { lowS: CURVE.lowS }
|
||||||
): boolean {
|
): boolean {
|
||||||
|
let _sig: Signature | undefined = undefined;
|
||||||
try {
|
try {
|
||||||
if (signature instanceof Signature) {
|
if (signature instanceof Signature) {
|
||||||
signature.assertValidity();
|
signature.assertValidity();
|
||||||
|
_sig = signature;
|
||||||
} else {
|
} else {
|
||||||
// Signature can be represented in 2 ways: compact (64-byte) & DER (variable-length).
|
// Signature can be represented in 2 ways: compact (64-byte) & DER (variable-length).
|
||||||
// Since DER can also be 64 bytes, we check for it first.
|
// Since DER can also be 64 bytes, we check for it first.
|
||||||
try {
|
try {
|
||||||
signature = Signature.fromDER(signature as Hex);
|
_sig = Signature.fromDER(signature as Hex);
|
||||||
} catch (derError) {
|
} catch (derError) {
|
||||||
if (!(derError instanceof DERError)) throw derError;
|
if (!(derError instanceof DERError)) throw derError;
|
||||||
signature = Signature.fromCompact(signature as Hex);
|
_sig = Signature.fromCompact(signature as Hex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msgHash = ut.ensureBytes(msgHash);
|
msgHash = ut.ensureBytes(msgHash);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (opts.lowS && signature.hasHighS()) return false;
|
if (opts.lowS && _sig.hasHighS()) return false;
|
||||||
let P;
|
let P;
|
||||||
try {
|
try {
|
||||||
P = normalizePublicKey(publicKey);
|
P = normalizePublicKey(publicKey);
|
||||||
@ -1261,7 +1190,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const { n } = CURVE;
|
const { n } = CURVE;
|
||||||
const { r, s } = signature;
|
const { r, s } = _sig;
|
||||||
|
|
||||||
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
const h = bits2int_modN(msgHash); // Cannot use fields methods, since it is group element
|
||||||
const sinv = mod.invert(s, n); // s^-1
|
const sinv = mod.invert(s, n); // s^-1
|
||||||
@ -1271,7 +1200,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
|
|
||||||
// Some implementations compare R.x in projective, without inversion.
|
// Some implementations compare R.x in projective, without inversion.
|
||||||
// The speed-up is <5%, so we don't complicate the code.
|
// The speed-up is <5%, so we don't complicate the code.
|
||||||
const R = Point.BASE.multiplyAndAddUnsafe(P, u1, u2);
|
const R = ProjectivePoint.BASE.multiplyAndAddUnsafe(P, u1, u2)?.toAffine();
|
||||||
if (!R) return false;
|
if (!R) return false;
|
||||||
const v = mod.mod(R.x, n);
|
const v = mod.mod(R.x, n);
|
||||||
return v === r;
|
return v === r;
|
||||||
@ -1283,7 +1212,7 @@ export function weierstrass(curveDef: CurveType): CurveFn {
|
|||||||
sign,
|
sign,
|
||||||
signUnhashed,
|
signUnhashed,
|
||||||
verify,
|
verify,
|
||||||
Point,
|
// Point,
|
||||||
ProjectivePoint,
|
ProjectivePoint,
|
||||||
Signature,
|
Signature,
|
||||||
utils,
|
utils,
|
||||||
|
@ -28,7 +28,6 @@ import {
|
|||||||
} from './abstract/utils.js';
|
} from './abstract/utils.js';
|
||||||
// Types
|
// Types
|
||||||
import {
|
import {
|
||||||
PointType,
|
|
||||||
ProjectivePointType,
|
ProjectivePointType,
|
||||||
ProjectiveConstructor,
|
ProjectiveConstructor,
|
||||||
mapToCurveSimpleSWU,
|
mapToCurveSimpleSWU,
|
||||||
@ -1035,7 +1034,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
return { x: Fp.create(x), y: Fp.create(y) };
|
return { x: Fp.create(x), y: Fp.create(y) };
|
||||||
} else if (bytes.length === 96) {
|
} else if (bytes.length === 96) {
|
||||||
// Check if the infinity flag is set
|
// Check if the infinity flag is set
|
||||||
if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.Point.ZERO;
|
if ((bytes[0] & (1 << 6)) !== 0) return bls12_381.G1.ProjectivePoint.ZERO;
|
||||||
const x = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
const x = bytesToNumberBE(bytes.slice(0, Fp.BYTES));
|
||||||
const y = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
const y = bytesToNumberBE(bytes.slice(Fp.BYTES));
|
||||||
return { x: Fp.create(x), y: Fp.create(y) };
|
return { x: Fp.create(x), y: Fp.create(y) };
|
||||||
@ -1188,7 +1187,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): PointType<Fp2> {
|
decode(hex: Hex): ProjectivePointType<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;
|
||||||
@ -1198,7 +1197,7 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const z2 = bytesToNumberBE(hex.slice(half));
|
const z2 = bytesToNumberBE(hex.slice(half));
|
||||||
// Indicates the infinity point
|
// Indicates the infinity point
|
||||||
const bflag1 = bitGet(z1, I_BIT_POS);
|
const bflag1 = bitGet(z1, I_BIT_POS);
|
||||||
if (bflag1 === 1n) return bls12_381.G2.Point.ZERO;
|
if (bflag1 === 1n) return bls12_381.G2.ProjectivePoint.ZERO;
|
||||||
|
|
||||||
const x1 = Fp.create(z1 & Fp.MASK);
|
const x1 = Fp.create(z1 & Fp.MASK);
|
||||||
const x2 = Fp.create(z2);
|
const x2 = Fp.create(z2);
|
||||||
@ -1215,14 +1214,14 @@ export const bls12_381: CurveFn<Fp, Fp2, Fp6, Fp12> = bls({
|
|||||||
const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1;
|
const isGreater = y1 > 0n && (y1 * 2n) / P !== aflag1;
|
||||||
const isZero = y1 === 0n && (y0 * 2n) / P !== aflag1;
|
const isZero = y1 === 0n && (y0 * 2n) / P !== aflag1;
|
||||||
if (isGreater || isZero) y = Fp2.negate(y);
|
if (isGreater || isZero) y = Fp2.negate(y);
|
||||||
const point = new bls12_381.G2.Point(x, y);
|
const point = bls12_381.G2.ProjectivePoint.fromAffine({ x, y });
|
||||||
point.assertValidity();
|
point.assertValidity();
|
||||||
return point;
|
return point;
|
||||||
},
|
},
|
||||||
encode(point: PointType<Fp2>) {
|
encode(point: ProjectivePointType<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.Point.ZERO))
|
if (point.equals(bls12_381.G2.ProjectivePoint.ZERO))
|
||||||
return concatBytes(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
return concatBytes(COMPRESSED_ZERO, numberToBytesBE(0n, Fp.BYTES));
|
||||||
const { re: x0, im: x1 } = Fp2.reim(point.x);
|
const { re: x0, im: x1 } = Fp2.reim(point.x);
|
||||||
const { re: y0, im: y1 } = Fp2.reim(point.y);
|
const { re: y0, im: y1 } = Fp2.reim(point.y);
|
||||||
|
@ -38,7 +38,7 @@ export const P256 = createCurve(
|
|||||||
export const secp256r1 = P256;
|
export const secp256r1 = P256;
|
||||||
|
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||||
secp256r1.Point,
|
secp256r1.ProjectivePoint,
|
||||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||||
{
|
{
|
||||||
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
DST: 'P256_XMD:SHA-256_SSWU_RO_',
|
||||||
|
@ -42,7 +42,7 @@ export const P384 = createCurve({
|
|||||||
export const secp384r1 = P384;
|
export const secp384r1 = P384;
|
||||||
|
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||||
secp384r1.Point,
|
secp384r1.ProjectivePoint,
|
||||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||||
{
|
{
|
||||||
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
DST: 'P384_XMD:SHA-384_SSWU_RO_',
|
||||||
|
@ -53,7 +53,7 @@ export const P521 = createCurve({
|
|||||||
export const secp521r1 = P521;
|
export const secp521r1 = P521;
|
||||||
|
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||||
secp521r1.Point,
|
secp521r1.ProjectivePoint,
|
||||||
(scalars: bigint[]) => mapSWU(scalars[0]),
|
(scalars: bigint[]) => mapSWU(scalars[0]),
|
||||||
{
|
{
|
||||||
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
DST: 'P521_XMD:SHA-512_SSWU_RO_',
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
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 { PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
import { ProjectivePointType as PointType, mapToCurveSimpleSWU } from './abstract/weierstrass.js';
|
||||||
import {
|
import {
|
||||||
ensureBytes,
|
ensureBytes,
|
||||||
concatBytes,
|
concatBytes,
|
||||||
@ -148,7 +148,7 @@ export const secp256k1 = createCurve(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
const { hashToCurve, encodeToCurve } = htf.hashToCurve(
|
||||||
secp256k1.Point,
|
secp256k1.ProjectivePoint,
|
||||||
(scalars: bigint[]) => {
|
(scalars: bigint[]) => {
|
||||||
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
const { x, y } = mapSWU(Fp.create(scalars[0]));
|
||||||
return isoMap(x, y);
|
return isoMap(x, y);
|
||||||
@ -173,7 +173,7 @@ const normalizePrivateKey = secp256k1.utils._normalizePrivateKey;
|
|||||||
|
|
||||||
// TODO: export?
|
// TODO: export?
|
||||||
function normalizePublicKey(publicKey: Hex | PointType<bigint>): PointType<bigint> {
|
function normalizePublicKey(publicKey: Hex | PointType<bigint>): PointType<bigint> {
|
||||||
if (publicKey instanceof secp256k1.Point) {
|
if (publicKey instanceof secp256k1.ProjectivePoint) {
|
||||||
publicKey.assertValidity();
|
publicKey.assertValidity();
|
||||||
return publicKey;
|
return publicKey;
|
||||||
} else {
|
} else {
|
||||||
@ -187,7 +187,7 @@ function normalizePublicKey(publicKey: Hex | PointType<bigint>): PointType<bigin
|
|||||||
const isYOdd = (y & _1n) === _1n;
|
const isYOdd = (y & _1n) === _1n;
|
||||||
// Schnorr
|
// Schnorr
|
||||||
if (isYOdd) y = secp256k1.CURVE.Fp.negate(y);
|
if (isYOdd) y = secp256k1.CURVE.Fp.negate(y);
|
||||||
const point = new secp256k1.Point(x, y);
|
const point = secp256k1.ProjectivePoint.fromAffine({ x, y });
|
||||||
point.assertValidity();
|
point.assertValidity();
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
@ -250,7 +250,7 @@ class SchnorrSignature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function schnorrGetScalar(priv: bigint) {
|
function schnorrGetScalar(priv: bigint) {
|
||||||
const point = secp256k1.Point.fromPrivateKey(priv);
|
const point = secp256k1.ProjectivePoint.fromPrivateKey(priv);
|
||||||
const scalar = point.hasEvenY() ? priv : secp256k1.CURVE.n - priv;
|
const scalar = point.hasEvenY() ? priv : secp256k1.CURVE.n - priv;
|
||||||
return { point, scalar, x: toRawX(point) };
|
return { point, scalar, x: toRawX(point) };
|
||||||
}
|
}
|
||||||
@ -301,12 +301,12 @@ function schnorrVerify(signature: Hex, message: Hex, publicKey: Hex): boolean {
|
|||||||
// Finalize
|
// Finalize
|
||||||
// R = s⋅G - e⋅P
|
// R = s⋅G - e⋅P
|
||||||
// -eP == (n-e)P
|
// -eP == (n-e)P
|
||||||
const R = secp256k1.Point.BASE.multiplyAndAddUnsafe(
|
const R = secp256k1.ProjectivePoint.BASE.multiplyAndAddUnsafe(
|
||||||
P,
|
P,
|
||||||
normalizePrivateKey(s),
|
normalizePrivateKey(s),
|
||||||
mod(-e, secp256k1.CURVE.n)
|
mod(-e, secp256k1.CURVE.n)
|
||||||
);
|
);
|
||||||
if (!R || !R.hasEvenY() || R.x !== r) return false;
|
if (!R || !R.hasEvenY() || R.toAffine().x !== r) return false;
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return false;
|
return false;
|
||||||
@ -317,7 +317,7 @@ export const schnorr = {
|
|||||||
Signature: SchnorrSignature,
|
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(secp256k1.Point.fromPrivateKey(privateKey)),
|
toRawX(secp256k1.ProjectivePoint.fromPrivateKey(privateKey)),
|
||||||
sign: schnorrSign,
|
sign: schnorrSign,
|
||||||
verify: schnorrVerify,
|
verify: schnorrVerify,
|
||||||
};
|
};
|
||||||
|
15
src/stark.ts
15
src/stark.ts
@ -113,11 +113,10 @@ function verify0x(signature: Hex, msgHash: Hex, pubKey: Hex) {
|
|||||||
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
|
return starkCurve.verify(sig, ensureBytes0x(msgHash), ensureBytes0x(pubKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
const { CURVE, Point, ProjectivePoint, Signature } = starkCurve;
|
const { CURVE, ProjectivePoint, Signature } = starkCurve;
|
||||||
export const utils = starkCurve.utils;
|
export const utils = starkCurve.utils;
|
||||||
export {
|
export {
|
||||||
CURVE,
|
CURVE,
|
||||||
Point,
|
|
||||||
Signature,
|
Signature,
|
||||||
ProjectivePoint,
|
ProjectivePoint,
|
||||||
getPublicKey0x as getPublicKey,
|
getPublicKey0x as getPublicKey,
|
||||||
@ -180,23 +179,23 @@ export function getAccountPath(
|
|||||||
|
|
||||||
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
// https://docs.starkware.co/starkex/pedersen-hash-function.html
|
||||||
const PEDERSEN_POINTS_AFFINE = [
|
const PEDERSEN_POINTS_AFFINE = [
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
2089986280348253421170679821480865132823066470938446095505822317253594081284n,
|
||||||
1713931329540660377023406109199410414810705867260802078187082345529207694986n
|
1713931329540660377023406109199410414810705867260802078187082345529207694986n
|
||||||
),
|
),
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
996781205833008774514500082376783249102396023663454813447423147977397232763n,
|
996781205833008774514500082376783249102396023663454813447423147977397232763n,
|
||||||
1668503676786377725805489344771023921079126552019160156920634619255970485781n
|
1668503676786377725805489344771023921079126552019160156920634619255970485781n
|
||||||
),
|
),
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
2251563274489750535117886426533222435294046428347329203627021249169616184184n,
|
2251563274489750535117886426533222435294046428347329203627021249169616184184n,
|
||||||
1798716007562728905295480679789526322175868328062420237419143593021674992973n
|
1798716007562728905295480679789526322175868328062420237419143593021674992973n
|
||||||
),
|
),
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
2138414695194151160943305727036575959195309218611738193261179310511854807447n,
|
2138414695194151160943305727036575959195309218611738193261179310511854807447n,
|
||||||
113410276730064486255102093846540133784865286929052426931474106396135072156n
|
113410276730064486255102093846540133784865286929052426931474106396135072156n
|
||||||
),
|
),
|
||||||
new Point(
|
new ProjectivePoint(
|
||||||
2379962749567351885752724891227938183011949129833673362440656643086021394946n,
|
2379962749567351885752724891227938183011949129833673362440656643086021394946n,
|
||||||
776496453633298175483985398648758586525933812536653089401905292063708816422n
|
776496453633298175483985398648758586525933812536653089401905292063708816422n
|
||||||
),
|
),
|
||||||
@ -253,7 +252,7 @@ export function pedersen(x: PedersenArg, y: PedersenArg) {
|
|||||||
let point: ProjectivePoint = PEDERSEN_POINTS[0];
|
let point: ProjectivePoint = PEDERSEN_POINTS[0];
|
||||||
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
point = pedersenSingle(point, x, PEDERSEN_POINTS1);
|
||||||
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
point = pedersenSingle(point, y, PEDERSEN_POINTS2);
|
||||||
return bytesToHexEth(point.toAffine().toRawBytes(true).slice(1));
|
return bytesToHexEth(point.toRawBytes(true).slice(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hashChain(data: PedersenArg[], fn = pedersen) {
|
export function hashChain(data: PedersenArg[], fn = pedersen) {
|
||||||
|
@ -46,7 +46,7 @@ should('wychenproof ECDSA vectors', () => {
|
|||||||
if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') {
|
if (group.key.curve === 'secp224r1' && group.sha !== 'SHA-224') {
|
||||||
if (group.sha === 'SHA-256') CURVE = CURVE.create(sha256);
|
if (group.sha === 'SHA-256') CURVE = CURVE.create(sha256);
|
||||||
}
|
}
|
||||||
const pubKey = CURVE.Point.fromHex(group.key.uncompressed);
|
const pubKey = CURVE.ProjectivePoint.fromHex(group.key.uncompressed);
|
||||||
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
||||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
||||||
for (const test of group.tests) {
|
for (const test of group.tests) {
|
||||||
@ -87,7 +87,7 @@ should('wychenproof ECDH vectors', () => {
|
|||||||
for (const test of group.tests) {
|
for (const test of group.tests) {
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
try {
|
try {
|
||||||
const pub = CURVE.Point.fromHex(test.public);
|
const pub = CURVE.ProjectivePoint.fromHex(test.public);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
||||||
throw e;
|
throw e;
|
||||||
@ -147,7 +147,7 @@ for (const name in WYCHEPROOF_ECDH) {
|
|||||||
for (const test of group.tests) {
|
for (const test of group.tests) {
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
try {
|
try {
|
||||||
const pub = curve.Point.fromHex(test.public);
|
const pub = curve.ProjectivePoint.fromHex(test.public);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
if (e.message.includes('Point.fromHex: received invalid point.')) continue;
|
||||||
throw e;
|
throw e;
|
||||||
@ -309,7 +309,7 @@ const WYCHEPROOF_ECDSA = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function runWycheproof(name, CURVE, group, index) {
|
function runWycheproof(name, CURVE, group, index) {
|
||||||
const pubKey = CURVE.Point.fromHex(group.key.uncompressed);
|
const pubKey = CURVE.ProjectivePoint.fromHex(group.key.uncompressed);
|
||||||
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
deepStrictEqual(pubKey.x, BigInt(`0x${group.key.wx}`));
|
||||||
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
deepStrictEqual(pubKey.y, BigInt(`0x${group.key.wy}`));
|
||||||
for (const test of group.tests) {
|
for (const test of group.tests) {
|
||||||
@ -367,7 +367,7 @@ should('RFC6979', () => {
|
|||||||
const curve = NIST[v.curve];
|
const curve = NIST[v.curve];
|
||||||
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
deepStrictEqual(curve.CURVE.n, hexToBigint(v.q));
|
||||||
const pubKey = curve.getPublicKey(v.private);
|
const pubKey = curve.getPublicKey(v.private);
|
||||||
const pubPoint = curve.Point.fromHex(pubKey);
|
const pubPoint = curve.ProjectivePoint.fromHex(pubKey);
|
||||||
deepStrictEqual(pubPoint.x, hexToBigint(v.Ux));
|
deepStrictEqual(pubPoint.x, hexToBigint(v.Ux));
|
||||||
deepStrictEqual(pubPoint.y, hexToBigint(v.Uy));
|
deepStrictEqual(pubPoint.y, hexToBigint(v.Uy));
|
||||||
for (const c of v.cases) {
|
for (const c of v.cases) {
|
||||||
|
@ -13,6 +13,7 @@ import { hexToBytes, bytesToHex } from '@noble/hashes/utils';
|
|||||||
|
|
||||||
const hex = bytesToHex;
|
const hex = bytesToHex;
|
||||||
const secp = secp256k1;
|
const secp = secp256k1;
|
||||||
|
const Point = secp.ProjectivePoint;
|
||||||
const privatesTxt = readFileSync('./test/vectors/privates-2.txt', 'utf-8');
|
const privatesTxt = readFileSync('./test/vectors/privates-2.txt', 'utf-8');
|
||||||
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
|
const schCsv = readFileSync('./test/vectors/schnorr.csv', 'utf-8');
|
||||||
|
|
||||||
@ -37,15 +38,15 @@ describe('secp256k1', () => {
|
|||||||
.filter((line) => line)
|
.filter((line) => line)
|
||||||
.map((line) => line.split(':'));
|
.map((line) => line.split(':'));
|
||||||
for (let [priv, x, y] of data) {
|
for (let [priv, x, y] of data) {
|
||||||
const point = secp.Point.fromPrivateKey(BigInt(priv));
|
const point = Point.fromPrivateKey(BigInt(priv));
|
||||||
deepStrictEqual(toBEHex(point.x), x);
|
deepStrictEqual(toBEHex(point.x), x);
|
||||||
deepStrictEqual(toBEHex(point.y), y);
|
deepStrictEqual(toBEHex(point.y), y);
|
||||||
|
|
||||||
const point2 = secp.Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
const point2 = Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
||||||
deepStrictEqual(toBEHex(point2.x), x);
|
deepStrictEqual(toBEHex(point2.x), x);
|
||||||
deepStrictEqual(toBEHex(point2.y), y);
|
deepStrictEqual(toBEHex(point2.y), y);
|
||||||
|
|
||||||
const point3 = secp.Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
const point3 = Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
||||||
deepStrictEqual(toBEHex(point3.x), x);
|
deepStrictEqual(toBEHex(point3.x), x);
|
||||||
deepStrictEqual(toBEHex(point3.y), y);
|
deepStrictEqual(toBEHex(point3.y), y);
|
||||||
}
|
}
|
||||||
@ -62,15 +63,15 @@ describe('secp256k1', () => {
|
|||||||
.filter((line) => line)
|
.filter((line) => line)
|
||||||
.map((line) => line.split(':'));
|
.map((line) => line.split(':'));
|
||||||
for (let [priv, x, y] of data) {
|
for (let [priv, x, y] of data) {
|
||||||
const point = secp.Point.fromPrivateKey(BigInt(priv));
|
const point = Point.fromPrivateKey(BigInt(priv));
|
||||||
deepStrictEqual(toBEHex(point.x), x);
|
deepStrictEqual(toBEHex(point.x), x);
|
||||||
deepStrictEqual(toBEHex(point.y), y);
|
deepStrictEqual(toBEHex(point.y), y);
|
||||||
|
|
||||||
const point2 = secp.Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
const point2 = Point.fromHex(secp.getPublicKey(toBEHex(BigInt(priv))));
|
||||||
deepStrictEqual(toBEHex(point2.x), x);
|
deepStrictEqual(toBEHex(point2.x), x);
|
||||||
deepStrictEqual(toBEHex(point2.y), y);
|
deepStrictEqual(toBEHex(point2.y), y);
|
||||||
|
|
||||||
const point3 = secp.Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
const point3 = Point.fromHex(secp.getPublicKey(hexToBytes(toBEHex(BigInt(priv)))));
|
||||||
deepStrictEqual(toBEHex(point3.x), x);
|
deepStrictEqual(toBEHex(point3.x), x);
|
||||||
deepStrictEqual(toBEHex(point3.y), y);
|
deepStrictEqual(toBEHex(point3.y), y);
|
||||||
}
|
}
|
||||||
@ -81,9 +82,9 @@ describe('secp256k1', () => {
|
|||||||
for (const vector of points.valid.isPoint) {
|
for (const vector of points.valid.isPoint) {
|
||||||
const { P, expected } = vector;
|
const { P, expected } = vector;
|
||||||
if (expected) {
|
if (expected) {
|
||||||
secp.Point.fromHex(P);
|
Point.fromHex(P);
|
||||||
} else {
|
} else {
|
||||||
throws(() => secp.Point.fromHex(P));
|
throws(() => Point.fromHex(P));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -91,7 +92,7 @@ describe('secp256k1', () => {
|
|||||||
should('.fromPrivateKey()', () => {
|
should('.fromPrivateKey()', () => {
|
||||||
for (const vector of points.valid.pointFromScalar) {
|
for (const vector of points.valid.pointFromScalar) {
|
||||||
const { d, expected } = vector;
|
const { d, expected } = vector;
|
||||||
let p = secp.Point.fromPrivateKey(d);
|
let p = Point.fromPrivateKey(d);
|
||||||
deepStrictEqual(p.toHex(true), expected);
|
deepStrictEqual(p.toHex(true), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -99,26 +100,26 @@ describe('secp256k1', () => {
|
|||||||
should('#toHex(compressed)', () => {
|
should('#toHex(compressed)', () => {
|
||||||
for (const vector of points.valid.pointCompress) {
|
for (const vector of points.valid.pointCompress) {
|
||||||
const { P, compress, expected } = vector;
|
const { P, compress, expected } = vector;
|
||||||
let p = secp.Point.fromHex(P);
|
let p = Point.fromHex(P);
|
||||||
deepStrictEqual(p.toHex(compress), expected);
|
deepStrictEqual(p.toHex(compress), expected);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
should('#toHex() roundtrip (failed case)', () => {
|
should('#toHex() roundtrip (failed case)', () => {
|
||||||
const point1 =
|
const point1 =
|
||||||
secp.Point.fromPrivateKey(
|
Point.fromPrivateKey(
|
||||||
88572218780422190464634044548753414301110513745532121983949500266768436236425n
|
88572218780422190464634044548753414301110513745532121983949500266768436236425n
|
||||||
);
|
);
|
||||||
// const hex = point1.toHex(true);
|
// const hex = point1.toHex(true);
|
||||||
// deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
// deepStrictEqual(Point.fromHex(hex).toHex(true), hex);
|
||||||
});
|
});
|
||||||
|
|
||||||
should('#toHex() roundtrip', () => {
|
should('#toHex() roundtrip', () => {
|
||||||
fc.assert(
|
fc.assert(
|
||||||
fc.property(FC_BIGINT, (x) => {
|
fc.property(FC_BIGINT, (x) => {
|
||||||
const point1 = secp.Point.fromPrivateKey(x);
|
const point1 = Point.fromPrivateKey(x);
|
||||||
const hex = point1.toHex(true);
|
const hex = point1.toHex(true);
|
||||||
deepStrictEqual(secp.Point.fromHex(hex).toHex(true), hex);
|
deepStrictEqual(Point.fromHex(hex).toHex(true), hex);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -126,8 +127,8 @@ describe('secp256k1', () => {
|
|||||||
should('#add(other)', () => {
|
should('#add(other)', () => {
|
||||||
for (const vector of points.valid.pointAdd) {
|
for (const vector of points.valid.pointAdd) {
|
||||||
const { P, Q, expected } = vector;
|
const { P, Q, expected } = vector;
|
||||||
let p = secp.Point.fromHex(P);
|
let p = Point.fromHex(P);
|
||||||
let q = secp.Point.fromHex(Q);
|
let q = Point.fromHex(Q);
|
||||||
if (expected) {
|
if (expected) {
|
||||||
deepStrictEqual(p.add(q).toHex(true), expected);
|
deepStrictEqual(p.add(q).toHex(true), expected);
|
||||||
} else {
|
} else {
|
||||||
@ -141,7 +142,7 @@ describe('secp256k1', () => {
|
|||||||
should('#multiply(privateKey)', () => {
|
should('#multiply(privateKey)', () => {
|
||||||
for (const vector of points.valid.pointMultiply) {
|
for (const vector of points.valid.pointMultiply) {
|
||||||
const { P, d, expected } = vector;
|
const { P, d, expected } = vector;
|
||||||
const p = secp.Point.fromHex(P);
|
const p = Point.fromHex(P);
|
||||||
if (expected) {
|
if (expected) {
|
||||||
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected);
|
deepStrictEqual(p.multiply(hexToNumber(d)).toHex(true), expected);
|
||||||
} else {
|
} else {
|
||||||
@ -155,13 +156,13 @@ describe('secp256k1', () => {
|
|||||||
const { P, d } = vector;
|
const { P, d } = vector;
|
||||||
if (hexToNumber(d) < secp.CURVE.n) {
|
if (hexToNumber(d) < secp.CURVE.n) {
|
||||||
throws(() => {
|
throws(() => {
|
||||||
const p = secp.Point.fromHex(P);
|
const p = Point.fromHex(P);
|
||||||
p.multiply(hexToNumber(d)).toHex(true);
|
p.multiply(hexToNumber(d)).toHex(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
for (const num of [0n, 0, -1n, -1, 1.1]) {
|
||||||
throws(() => secp.Point.BASE.multiply(num));
|
throws(() => Point.BASE.multiply(num));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -280,7 +281,7 @@ describe('secp256k1', () => {
|
|||||||
const PRIV_KEY = 0x2n;
|
const PRIV_KEY = 0x2n;
|
||||||
const WRONG_PRIV_KEY = 0x22n;
|
const WRONG_PRIV_KEY = 0x22n;
|
||||||
const signature = secp.sign(MSG, PRIV_KEY);
|
const signature = secp.sign(MSG, PRIV_KEY);
|
||||||
const publicKey = secp.Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
|
const publicKey = Point.fromPrivateKey(WRONG_PRIV_KEY).toHex();
|
||||||
deepStrictEqual(publicKey.length, 66);
|
deepStrictEqual(publicKey.length, 66);
|
||||||
deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
|
deepStrictEqual(secp.verify(signature, MSG, publicKey), false);
|
||||||
});
|
});
|
||||||
@ -313,7 +314,7 @@ describe('secp256k1', () => {
|
|||||||
const r = 1n;
|
const r = 1n;
|
||||||
const s = 115792089237316195423570985008687907852837564279074904382605163141518162728904n;
|
const s = 115792089237316195423570985008687907852837564279074904382605163141518162728904n;
|
||||||
|
|
||||||
const pub = new secp.Point(x, y);
|
const pub = new Point(x, y);
|
||||||
const signature = new secp.Signature(2n, 2n);
|
const signature = new secp.Signature(2n, 2n);
|
||||||
signature.r = r;
|
signature.r = r;
|
||||||
signature.s = s;
|
signature.s = s;
|
||||||
@ -328,7 +329,7 @@ describe('secp256k1', () => {
|
|||||||
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
|
const y = 32670510020758816978083085130507043184471273380659243275938904335757337482424n;
|
||||||
const r = 104546003225722045112039007203142344920046999340768276760147352389092131869133n;
|
const r = 104546003225722045112039007203142344920046999340768276760147352389092131869133n;
|
||||||
const s = 96900796730960181123786672629079577025401317267213807243199432755332205217369n;
|
const s = 96900796730960181123786672629079577025401317267213807243199432755332205217369n;
|
||||||
const pub = new secp.Point(x, y);
|
const pub = new Point(x, y);
|
||||||
const sig = new secp.Signature(r, s);
|
const sig = new secp.Signature(r, s);
|
||||||
deepStrictEqual(secp.verify(sig, msg, pub), false);
|
deepStrictEqual(secp.verify(sig, msg, pub), false);
|
||||||
});
|
});
|
||||||
@ -338,7 +339,7 @@ describe('secp256k1', () => {
|
|||||||
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
|
const y = 17482644437196207387910659778872952193236850502325156318830589868678978890912n;
|
||||||
const r = 432420386565659656852420866390673177323n;
|
const r = 432420386565659656852420866390673177323n;
|
||||||
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
|
const s = 115792089237316195423570985008687907852837564279074904382605163141518161494334n;
|
||||||
const pub = new secp.Point(x, y);
|
const pub = new Point(x, y);
|
||||||
const sig = new secp.Signature(r, s);
|
const sig = new secp.Signature(r, s);
|
||||||
deepStrictEqual(secp.verify(sig, msg, pub, { strict: false }), true);
|
deepStrictEqual(secp.verify(sig, msg, pub, { strict: false }), true);
|
||||||
});
|
});
|
||||||
@ -376,7 +377,7 @@ describe('secp256k1', () => {
|
|||||||
should('recover public key from recovery bit', () => {
|
should('recover public key from recovery bit', () => {
|
||||||
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
const message = '00000000000000000000000000000000000000000000000000000000deadbeef';
|
||||||
const privateKey = 123456789n;
|
const privateKey = 123456789n;
|
||||||
const publicKey = secp.Point.fromHex(secp.getPublicKey(privateKey)).toHex(false);
|
const publicKey = Point.fromHex(secp.getPublicKey(privateKey)).toHex(false);
|
||||||
const sig = secp.sign(message, privateKey);
|
const sig = secp.sign(message, privateKey);
|
||||||
const recoveredPubkey = sig.recoverPublicKey(message);
|
const recoveredPubkey = sig.recoverPublicKey(message);
|
||||||
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
// const recoveredPubkey = secp.recoverPublicKey(message, signature, recovery);
|
||||||
@ -451,7 +452,7 @@ describe('secp256k1', () => {
|
|||||||
should('have proper curve equation in assertValidity()', () => {
|
should('have proper curve equation in assertValidity()', () => {
|
||||||
throws(() => {
|
throws(() => {
|
||||||
const { Fp } = secp.CURVE;
|
const { Fp } = secp.CURVE;
|
||||||
let point = new secp.Point(Fp.create(-2n), Fp.create(-1n));
|
let point = new Point(Fp.create(-2n), Fp.create(-1n));
|
||||||
point.assertValidity();
|
point.assertValidity();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -472,15 +473,15 @@ describe('secp256k1', () => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
pointAddScalar: (p, tweak, isCompressed) => {
|
pointAddScalar: (p, tweak, isCompressed) => {
|
||||||
const P = secp.Point.fromHex(p);
|
const P = Point.fromHex(p);
|
||||||
const t = normal(tweak);
|
const t = normal(tweak);
|
||||||
const Q = secp.Point.BASE.multiplyAndAddUnsafe(P, t, 1n);
|
const Q = Point.BASE.multiplyAndAddUnsafe(P, t, 1n);
|
||||||
if (!Q) throw new Error('Tweaked point at infinity');
|
if (!Q) throw new Error('Tweaked point at infinity');
|
||||||
return Q.toRawBytes(isCompressed);
|
return Q.toRawBytes(isCompressed);
|
||||||
},
|
},
|
||||||
|
|
||||||
pointMultiply: (p, tweak, isCompressed) => {
|
pointMultiply: (p, tweak, isCompressed) => {
|
||||||
const P = secp.Point.fromHex(p);
|
const P = Point.fromHex(p);
|
||||||
const h = typeof tweak === 'string' ? tweak : bytesToHex(tweak);
|
const h = typeof tweak === 'string' ? tweak : bytesToHex(tweak);
|
||||||
const t = BigInt(`0x${h}`);
|
const t = BigInt(`0x${h}`);
|
||||||
return P.multiply(t).toRawBytes(isCompressed);
|
return P.multiply(t).toRawBytes(isCompressed);
|
||||||
@ -528,7 +529,7 @@ describe('secp256k1', () => {
|
|||||||
|
|
||||||
should('wychenproof vectors', () => {
|
should('wychenproof vectors', () => {
|
||||||
for (let group of wp.testGroups) {
|
for (let group of wp.testGroups) {
|
||||||
const pubKey = secp.Point.fromHex(group.key.uncompressed);
|
const pubKey = Point.fromHex(group.key.uncompressed);
|
||||||
for (let test of group.tests) {
|
for (let test of group.tests) {
|
||||||
const m = secp.CURVE.hash(hexToBytes(test.msg));
|
const m = secp.CURVE.hash(hexToBytes(test.msg));
|
||||||
if (test.result === 'valid' || test.result === 'acceptable') {
|
if (test.result === 'valid' || test.result === 'acceptable') {
|
||||||
|
Loading…
Reference in New Issue
Block a user