Skip to content

Commit

Permalink
feat: importProcess replace elliptic with noble curve
Browse files Browse the repository at this point in the history
refactor
  • Loading branch information
ieow committed Sep 30, 2024
1 parent 83aceb8 commit 652c42c
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 345 deletions.
11 changes: 11 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@ export function hexToBigInt(hex: string): bigint {
return BigInt(hexWithPrefix);
}

export function bufferToBigInt(buffer: Buffer): bigint {
return hexToBigInt(buffer.toString("hex"));
}

export function bigIntUmod(a: bigint, m: bigint): bigint {
// return a % m;
return ((a % m) + m) % m;
}

export function bigIntPointToHexPoint(point: { x: bigint; y: bigint }) {
return {
x: point.x.toString(16).padStart(64, "0"),
y: point.y.toString(16).padStart(64, "0"),
};
}

// You'll also need to implement a modular inverse function for BigInt
export function modularInverse(a: bigint, m: bigint): bigint {
let [old_r, r] = [a, m];
Expand Down
209 changes: 166 additions & 43 deletions src/importProcess.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import BN from "bn.js";
import { curve, ec as EC } from "elliptic";
import { Group } from "@noble/curves/abstract/curve";
import { ed25519 } from "@noble/curves/ed25519";
import { secp256k1 } from "@noble/curves/secp256k1";

import { bigIntPointToHexPoint, bigIntUmod, generatePolynomial, getLagrangeCoeff, getShare, hexToBigInt } from "./helpers";
import { IData, IMockServer, RSSRound1Response, ServersInfo } from "./rss";
import { decrypt, ecPoint, encrypt, EncryptedMessage, generatePolynomial, getLagrangeCoeff, getShare, hexPoint, PointHex } from "./utils";
import { decrypt, encrypt, EncryptedMessage, PointHex } from "./utils";

export const importClientRound1 = async (params: {
importKey: BN;
export const importClientRound1Internal = async <T extends Group<T> & { x: bigint; y: bigint }>(params: {
importKey: bigint;
targetIndexes: number[];
serversInfo: ServersInfo;
tempPubKey: PointHex;
keyType: "secp256k1" | "ed25519";
constructPoint: (p: { x: string; y: string } | { x: bigint; y: bigint }) => T;
nbCurve: typeof secp256k1 | typeof ed25519;
}) => {
const { importKey, targetIndexes, serversInfo, tempPubKey, keyType } = params;
const { importKey, targetIndexes, serversInfo, tempPubKey, nbCurve, constructPoint } = params;

const curveN = nbCurve.CURVE.n;
const curveG = constructPoint({ x: nbCurve.CURVE.Gx, y: nbCurve.CURVE.Gy });

const ecCurve = new EC(keyType);
const curveN = ecCurve.n;
const curveG = ecCurve.g;
const randomBytes = nbCurve.utils.randomPrivateKey;
const generatePrivate = () => bigIntUmod(hexToBigInt(Buffer.from(randomBytes()).toString("hex")), curveN);

const generatePrivate = () => ecCurve.genKeyPair().getPrivate();
// front end also generates hierarchical secret sharing
// - calculate lagrange coeffs
const _finalLagrangeCoeffs = targetIndexes.map((target) => getLagrangeCoeff([0, 1], 0, target, curveN).umod(curveN));
const _finalLagrangeCoeffs = targetIndexes.map((target) => bigIntUmod(getLagrangeCoeff([0, 1], 0, target, curveN), curveN));
const _masterPolys = [];
const _masterPolyCommits = [];
const _serverPolys = [];
Expand All @@ -29,17 +34,17 @@ export const importClientRound1 = async (params: {

for (let i = 0; i < _finalLagrangeCoeffs.length; i++) {
const _lc = _finalLagrangeCoeffs[i];
const _m = generatePolynomial(1, _lc.mul(importKey).umod(curveN), generateRandomScalar);
const _m = generatePolynomial(1, bigIntUmod(_lc * importKey, curveN), generateRandomScalar);
_masterPolys.push(_m);
_masterPolyCommits.push(
_m.map((coeff) => {
const _gCoeff = curveG.mul(coeff);
return hexPoint(_gCoeff);
const _gCoeff = curveG.multiply(coeff);
return _gCoeff;
})
);
const _s = generatePolynomial(serversInfo.threshold - 1, getShare(_m, 1, curveN), generateRandomScalar);
const _s = generatePolynomial(serversInfo.threshold - 1, getShare(_m, 1n, curveN), generateRandomScalar);
_serverPolys.push(_s);
_serverPolyCommits.push(_s.map((coeff) => hexPoint(curveG.mul(coeff))));
_serverPolyCommits.push(_s.map((coeff) => curveG.multiply(coeff)));
}
const _serverEncs = [];
const _userEncs = [];
Expand All @@ -52,7 +57,7 @@ export const importClientRound1 = async (params: {
_userEncs.push(
await encrypt(
Buffer.from(`04${tempPubKey.x.padStart(64, "0")}${tempPubKey.y.padStart(64, "0")}`, "hex"),
Buffer.from(getShare(_masterPoly, 99, curveN).toString(16, 64), "hex")
Buffer.from(getShare(_masterPoly, 99n, curveN).toString(16).padStart(64, "0"), "hex")
)
);

Expand All @@ -63,16 +68,25 @@ export const importClientRound1 = async (params: {
_serverEnc.push(
await encrypt(
Buffer.from(`04${_pub.x.padStart(64, "0")}${_pub.y.padStart(64, "0")}`, "hex"),
Buffer.from(getShare(_serverPoly, j + 1, curveN).toString(16, 64), "hex")
Buffer.from(
getShare(_serverPoly, BigInt(j + 1), curveN)
.toString(16)
.padStart(64, "0"),
"hex"
)
)
);
}
}
const _data: IData = [];
for (let i = 0; i < targetIndexes.length; i++) {
_data.push({
master_poly_commits: _masterPolyCommits[i],
server_poly_commits: _serverPolyCommits[i],
master_poly_commits: _masterPolyCommits[i].map((pt) => {
return bigIntPointToHexPoint(pt);
}),
server_poly_commits: _serverPolyCommits[i].map((pt) => {
return bigIntPointToHexPoint(pt);
}),
target_encryptions: {
user_enc: _userEncs[i],
server_encs: _serverEncs[i],
Expand All @@ -82,22 +96,37 @@ export const importClientRound1 = async (params: {
return _data;
};

export const importClientRound2 = async (opts: {
export const importClientRound2Internal = async <T extends Group<T> & { x: bigint; y: bigint }>(opts: {
targetIndexes: number[];
rssRound1Responses: RSSRound1Response[];
serverThreshold: number;
serverEndpoints: string[] | IMockServer[];
factorPubs: PointHex[];
tempPrivKey: BN;
tempPrivKey: bigint;
dkgNewPub: PointHex;
tssPubKey: PointHex;
keyType: "secp256k1" | "ed25519";
constructPoint: (p: { x: string; y: string } | { x: bigint; y: bigint }) => T;
nbCurve: typeof secp256k1 | typeof ed25519;
}) => {
const { rssRound1Responses, targetIndexes, serverThreshold, serverEndpoints, factorPubs, tempPrivKey, dkgNewPub, tssPubKey, keyType } = opts;
const {
rssRound1Responses,
targetIndexes,
serverThreshold,
serverEndpoints,
factorPubs,
tempPrivKey,
dkgNewPub,
tssPubKey,
constructPoint,
nbCurve,
} = opts;

const ecCurve = new EC(keyType);
const curveN = ecCurve.n;
const curveG = ecCurve.g;
// const nbCurve = secp256k1;
const curveN = nbCurve.CURVE.n;
const curveG = constructPoint({ x: nbCurve.CURVE.Gx, y: nbCurve.CURVE.Gy });

type Point = T;

// sum up all master poly commits and sum up all server poly commits
const sums = targetIndexes.map((_, i) => {
Expand All @@ -108,22 +137,22 @@ export const importClientRound2 = async (opts: {
if (serverPolyCommits.length !== serverThreshold) throw new Error("incorrect number of coeffs for server poly commits");
}

let sumMasterPolyCommits: curve.base.BasePoint[] = [];
let sumServerPolyCommits: curve.base.BasePoint[] = [];
let sumMasterPolyCommits: Point[] = [];
let sumServerPolyCommits: Point[] = [];

for (let j = 0; j < rssRound1Responses.length; j++) {
const rssRound1ResponseData = rssRound1Responses[j].data[i];
const { master_poly_commits: masterPolyCommits, server_poly_commits: serverPolyCommits } = rssRound1ResponseData;
if (sumMasterPolyCommits.length === 0 && sumServerPolyCommits.length === 0) {
sumMasterPolyCommits = masterPolyCommits.map((p) => ecPoint(ecCurve, p));
sumServerPolyCommits = serverPolyCommits.map((p) => ecPoint(ecCurve, p));
sumMasterPolyCommits = masterPolyCommits.map((p) => constructPoint(p));
sumServerPolyCommits = serverPolyCommits.map((p) => constructPoint(p));
continue;
}
sumMasterPolyCommits = sumMasterPolyCommits.map((summedCommit, k) => {
return ecPoint(ecCurve, masterPolyCommits[k]).add(summedCommit);
return constructPoint(masterPolyCommits[k]).add(summedCommit);
});
sumServerPolyCommits = sumServerPolyCommits.map((summedCommit, k) => {
return ecPoint(ecCurve, serverPolyCommits[k]).add(summedCommit);
return constructPoint(serverPolyCommits[k]).add(summedCommit);
});
}

Expand All @@ -137,34 +166,39 @@ export const importClientRound2 = async (opts: {
targetIndexes.map((target, i) => {
const { mc, sc } = sums[i];
// check master poly commits are consistent with tssPubKey
const temp1 = ecPoint(ecCurve, dkgNewPub).mul(getLagrangeCoeff([1, target], 1, 0, curveN));
const temp2 = mc[0].mul(getLagrangeCoeff([1, target], target, 0, curveN));
const temp1 = constructPoint(dkgNewPub).multiply(getLagrangeCoeff([1, target], 1, 0, curveN));
const temp2 = mc[0].multiply(getLagrangeCoeff([1, target], target, 0, curveN));
const _tssPubKey = temp1.add(temp2);
if (!_tssPubKey.eq(ecPoint(ecCurve, tssPubKey))) throw new Error("master poly commits inconsistent with tssPubKey");
if (!_tssPubKey.equals(constructPoint(tssPubKey))) throw new Error("master poly commits inconsistent with tssPubKey");

// check server poly commits are consistent with master poly commits
if (!mc[0].add(mc[1]).eq(sc[0])) throw new Error("server poly commits inconsistent with master poly commits");
if (!mc[0].add(mc[1]).equals(sc[0])) throw new Error("server poly commits inconsistent with master poly commits");
return null;
});

// front end checks if decrypted user shares are consistent with poly commits
const privKeyBuffer = Buffer.from(tempPrivKey.toString(16, 64), "hex");
const privKeyBuffer = Buffer.from(tempPrivKey.toString(16).padStart(64, "0"), "hex");
const userShares = [];
for (let i = 0; i < targetIndexes.length; i++) {
const userEncs = rssRound1Responses.map((r) => r.data[i].target_encryptions.user_enc);
const userDecs = await Promise.all(userEncs.map((encMsg) => decrypt(privKeyBuffer, encMsg)));
const userShare = userDecs.map((userDec) => new BN(userDec)).reduce((acc, d) => acc.add(d).umod(curveN), new BN(0));
const userShare = userDecs
.map((userDec) => BigInt(`0x${Buffer.from(userDec).toString("hex")}`))
.reduce((acc, d) => bigIntUmod(acc + d, curveN), BigInt(0));
const { mc } = sums[i];
const gU = curveG.mul(userShare);
const _gU = mc[0].add(mc[1].mul(new BN(99))); // master poly evaluated at x = 99
if (!gU.eq(_gU)) throw new Error("decrypted user shares inconsistent with poly commits");
const gU = curveG.multiply(userShare);
const _gU = mc[0].add(mc[1].multiply(99n)); // master poly evaluated at x = 99
if (!gU.equals(_gU)) throw new Error("decrypted user shares inconsistent with poly commits");
userShares.push(userShare);
}

const userFactorEncs = await Promise.all(
userShares.map((userShare, i) => {
const pub = factorPubs[i];
return encrypt(Buffer.from(`04${pub.x.padStart(64, "0")}${pub.y.padStart(64, "0")}`, "hex"), Buffer.from(userShare.toString(16, 64), "hex"));
return encrypt(
Buffer.from(`04${pub.x.padStart(64, "0")}${pub.y.padStart(64, "0")}`, "hex"),
Buffer.from(userShare.toString(16).padStart(64, "0"), "hex")
);
})
);

Expand All @@ -186,5 +220,94 @@ export const importClientRound2 = async (opts: {
return serverEncsToSend;
});

return { sums, userFactorEncs, serverEncs };
return {
sums: sums.map((s) => ({
mc: s.mc.map((p) => bigIntPointToHexPoint(p)),
sc: s.sc.map((p) => bigIntPointToHexPoint(p)),
})),
userFactorEncs,
serverEncs,
};
};

export const importClientRound1 = async (params: {
importKey: bigint;
targetIndexes: number[];
serversInfo: ServersInfo;
tempPubKey: PointHex;
keyType: "secp256k1" | "ed25519";
}) => {
if (params.keyType === "secp256k1") {
const nbCurve = secp256k1;
const constructPoint = (p: { x: string; y: string } | { x: bigint; y: bigint }) => {
if (typeof p.x === "bigint" && typeof p.y === "bigint") {
return secp256k1.ProjectivePoint.fromAffine({ x: p.x, y: p.y });
} else if (typeof p.x === "string" && typeof p.y === "string") {
return secp256k1.ProjectivePoint.fromAffine({ x: hexToBigInt(p.x), y: hexToBigInt(p.y) });
}
};
return importClientRound1Internal({
...params,
constructPoint,
nbCurve,
});
} else if (params.keyType === "ed25519") {
const nbCurve = ed25519;
const constructPoint = (p: { x: string; y: string } | { x: bigint; y: bigint }) => {
if (typeof p.x === "bigint" && typeof p.y === "bigint") {
return nbCurve.ExtendedPoint.fromAffine({ x: p.x, y: p.y });
} else if (typeof p.x === "string" && typeof p.y === "string") {
return nbCurve.ExtendedPoint.fromAffine({ x: hexToBigInt(p.x), y: hexToBigInt(p.y) });
}
};
return importClientRound1Internal({
...params,
constructPoint,
nbCurve,
});
}
throw new Error(`unknown key type: ${params.keyType}`);
};

export const importClientRound2 = async (opts: {
targetIndexes: number[];
rssRound1Responses: RSSRound1Response[];
serverThreshold: number;
serverEndpoints: string[] | IMockServer[];
factorPubs: PointHex[];
tempPrivKey: bigint;
dkgNewPub: PointHex;
tssPubKey: PointHex;
keyType: "secp256k1" | "ed25519";
}) => {
if (opts.keyType === "secp256k1") {
const nbCurve = secp256k1;
const constructPoint = (p: { x: string; y: string } | { x: bigint; y: bigint }) => {
if (typeof p.x === "bigint" && typeof p.y === "bigint") {
return secp256k1.ProjectivePoint.fromAffine({ x: p.x, y: p.y });
} else if (typeof p.x === "string" && typeof p.y === "string") {
return secp256k1.ProjectivePoint.fromAffine({ x: hexToBigInt(p.x), y: hexToBigInt(p.y) });
}
};
return importClientRound2Internal({
...opts,
constructPoint,
nbCurve,
});
} else if (opts.keyType === "ed25519") {
const nbCurve = ed25519;
const constructPoint = (p: { x: string; y: string } | { x: bigint; y: bigint }) => {
if (typeof p.x === "bigint" && typeof p.y === "bigint") {
return nbCurve.ExtendedPoint.fromAffine({ x: p.x, y: p.y });
} else if (typeof p.x === "string" && typeof p.y === "string") {
return nbCurve.ExtendedPoint.fromAffine({ x: hexToBigInt(p.x), y: hexToBigInt(p.y) });
}
};
return importClientRound2Internal({
...opts,
constructPoint,
nbCurve,
});
}
throw new Error("Invalid keyType, only secp256k1 or ed25519 is supported");
};
Loading

0 comments on commit 652c42c

Please sign in to comment.