Skip to content

Commit

Permalink
feat: soft nonce implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
guru-web3 committed Feb 5, 2024
1 parent f232b2d commit cea9d89
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 37 deletions.
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/common-types/src/baseTypes/aggregateTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export interface IMetadata extends ISerializable {

nonce: number;

chainCode?: string;

getShareIndexesForPolynomial(polyID: PolynomialID): string[];
getLatestPublicPolynomial(): PublicPolynomial;
addTSSData(tssData: {
Expand All @@ -89,6 +91,7 @@ export interface IMetadata extends ISerializable {
factorEncs?: {
[factorPubID: string]: FactorEnc;
};
chainCode?: string;
}): void;
addPublicShare(polynomialID: PolynomialID, publicShare: PublicShare): void;
setGeneralStoreDomain(key: string, obj: unknown): void;
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@toruslabs/rss-client": "^1.5.0",
"@toruslabs/torus.js": "^11.0.6",
"bn.js": "^5.2.1",
"crypto": "^1.0.1",
"elliptic": "^6.5.4",
"json-stable-stringify": "^1.0.2"
},
Expand Down
49 changes: 43 additions & 6 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ import {
toPrivKeyECC,
} from "@tkey-mpc/common-types";
import { generatePrivate } from "@toruslabs/eccrypto";
import { keccak256 } from "@toruslabs/torus.js";
import BN from "bn.js";
import crypto from "crypto";
import stringify from "json-stable-stringify";

import AuthMetadata from "./authMetadata";
Expand All @@ -66,7 +68,6 @@ import {
lagrangeInterpolation,
} from "./lagrangeInterpolatePolynomial";
import Metadata from "./metadata";

// TODO: handle errors for get and set with retries

class ThresholdKey implements ITKey {
Expand Down Expand Up @@ -226,6 +227,19 @@ class ThresholdKey implements ITKey {
throw CoreError.metadataUndefined();
}

computeNonce(index: number) {
// generation should occur during tkey.init, fails if chaincode is absent
const { chainCode } = this.metadata;
if (!chainCode) {
throw CoreError.default("chainCode is absent, required for nonce generation");
}
return new BN(keccak256(Buffer.from(`${index}${chainCode}`)).slice(2), "hex").umod(ecCurve.curve.n);
}

generateSalt(length = 32) {
return crypto.randomBytes(length).toString("hex");
}

async initialize(params?: {
withShare?: ShareStore;
importKey?: BN;
Expand Down Expand Up @@ -300,7 +314,8 @@ class ThresholdKey implements ITKey {
});
if (useTSS) {
const { factorEncs, factorPubs, tssPolyCommits } = await this._initializeNewTSSKey(this.tssTag, deviceTSSShare, factorPub, deviceTSSIndex);
this.metadata.addTSSData({ tssTag: this.tssTag, tssNonce: 0, tssPolyCommits, factorPubs, factorEncs });
const chainCode = this.generateSalt();
this.metadata.addTSSData({ tssTag: this.tssTag, tssNonce: 0, tssPolyCommits, factorPubs, factorEncs, chainCode });
}
return this.getKeyDetails();
}
Expand Down Expand Up @@ -385,7 +400,7 @@ class ThresholdKey implements ITKey {
* getTSSShare accepts a factorKey and returns the TSS share based on the factor encrypted TSS shares in the metadata
* @param factorKey - factor key
*/
async getTSSShare(factorKey: BN, opts?: { threshold: number }): Promise<{ tssIndex: number; tssShare: BN }> {
async getTSSShare(factorKey: BN, opts?: { threshold: number; accountIndex?: number }): Promise<{ tssIndex: number; tssShare: BN }> {
if (!this.privKey) throw CoreError.default("tss share cannot be returned until you've reconstructed tkey");
const factorPub = getPubKeyPoint(factorKey);
const factorEncs = this.getFactorEncs(factorPub);
Expand All @@ -407,6 +422,7 @@ class ThresholdKey implements ITKey {

const userDec = tssShareBNs[0];

const { threshold, accountIndex } = opts || {};
if (type === "direct") {
const tssSharePub = ecCurve.g.mul(userDec);
const tssCommitA0 = ecCurve.keyFromPublic({ x: tssCommits[0].x.toString(16, 64), y: tssCommits[0].y.toString(16, 64) }).getPublic();
Expand All @@ -416,6 +432,11 @@ class ThresholdKey implements ITKey {
_tssSharePub = _tssSharePub.add(tssCommitA1);
}
if (tssSharePub.getX().cmp(_tssSharePub.getX()) === 0 && tssSharePub.getY().cmp(_tssSharePub.getY()) === 0) {
if (accountIndex && accountIndex > 0) {
const nonce = this.computeNonce(accountIndex);
const derivedShare = userDec.add(nonce).umod(ecCurve.n);
return { tssIndex, tssShare: derivedShare };
}
return { tssIndex, tssShare: userDec };
}
throw new Error("user decryption does not match tss commitments...");
Expand All @@ -425,8 +446,6 @@ class ThresholdKey implements ITKey {
const serverDecs = tssShareBNs.slice(1); // 5 elems
const serverIndexes = new Array(serverDecs.length).fill(null).map((_, i) => i + 1);

const { threshold } = opts || {};

const combis = kCombinations(serverDecs.length, threshold || Math.ceil(serverDecs.length / 2));
for (let i = 0; i < combis.length; i++) {
const combi = combis[i];
Expand All @@ -445,6 +464,14 @@ class ThresholdKey implements ITKey {
for (let j = 0; j < tssIndex; j++) {
_tssSharePub = _tssSharePub.add(tssCommitA1);
}
if (accountIndex && accountIndex > 0) {
const nonce = this.computeNonce(accountIndex);
const derivedShare = tssShare.add(nonce).umod(ecCurve.n);
if (tssSharePub.getX().cmp(_tssSharePub.getX()) === 0 && tssSharePub.getY().cmp(_tssSharePub.getY()) === 0) {
return { tssIndex, tssShare: derivedShare };
}
}

if (tssSharePub.getX().cmp(_tssSharePub.getX()) === 0 && tssSharePub.getY().cmp(_tssSharePub.getY()) === 0) {
return { tssIndex, tssShare };
}
Expand All @@ -461,7 +488,17 @@ class ThresholdKey implements ITKey {
return tssPolyCommits;
}

getTSSPub(): Point {
getTSSPub(accountIndex?: number): Point {
if (accountIndex && accountIndex > 0) {
const nonce = this.computeNonce(accountIndex);
// we need to add the pub key nonce to the tssPub
const noncePub = ecCurve.keyFromPrivate(nonce.toString("hex")).getPublic();
const pubKeyPoint = ecCurve
.keyFromPublic({ x: this.getTSSCommits()[0].x.toString("hex"), y: this.getTSSCommits()[0].y.toString("hex") })
.getPublic();
const dervicepubKeyPoint = pubKeyPoint.add(noncePub);
return new Point(dervicepubKeyPoint.getX().toString("hex"), dervicepubKeyPoint.getY().toString("hex"));
}
return this.getTSSCommits()[0];
}

Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class Metadata implements IMetadata {
};
};

// salt
chainCode?: string;

constructor(input: Point) {
this.tssPolyCommits = {};
this.tssNonces = {};
Expand All @@ -84,7 +87,7 @@ class Metadata implements IMetadata {
}

static fromJSON(value: StringifiedType): Metadata {
const { pubKey, polyIDList, generalStore, tkeyStore, scopedStore, nonce, tssNonces, tssPolyCommits, factorPubs, factorEncs } = value;
const { pubKey, polyIDList, generalStore, tkeyStore, scopedStore, nonce, tssNonces, tssPolyCommits, factorPubs, factorEncs, chainCode } = value;
const point = Point.fromCompressedPub(pubKey);
const metadata = new Metadata(point);
const unserializedPolyIDList: PolyIDAndShares[] = [];
Expand All @@ -93,6 +96,7 @@ class Metadata implements IMetadata {
if (tkeyStore) metadata.tkeyStore = tkeyStore;
if (scopedStore) metadata.scopedStore = scopedStore;
if (nonce) metadata.nonce = nonce;
if (chainCode) metadata.chainCode = chainCode;
if (tssPolyCommits) {
metadata.tssPolyCommits = {};
for (const key in tssPolyCommits) {
Expand Down Expand Up @@ -186,15 +190,20 @@ class Metadata implements IMetadata {
tssNonce?: number;
tssPolyCommits?: Point[];
factorPubs?: Point[];
accountIndex?: number;
factorEncs?: {
[factorPubID: string]: FactorEnc;
};
chainCode?: string;
}): void {
const { tssTag, tssNonce, tssPolyCommits, factorPubs, factorEncs } = tssData;
const { tssTag, tssNonce, tssPolyCommits, factorPubs, factorEncs, chainCode } = tssData;
if (tssNonce !== undefined) this.tssNonces[tssTag] = tssNonce;
if (tssPolyCommits) this.tssPolyCommits[tssTag] = tssPolyCommits;
if (factorPubs) this.factorPubs[tssTag] = factorPubs;
if (factorEncs) this.factorEncs[tssTag] = factorEncs;
if (chainCode && !this.chainCode) {
this.chainCode = chainCode;
}
}

// appends shares and public polynomial to metadata.
Expand Down Expand Up @@ -338,6 +347,7 @@ class Metadata implements IMetadata {
...(this.tssPolyCommits && { tssPolyCommits: this.tssPolyCommits }),
...(this.factorPubs && { factorPubs: this.factorPubs }),
...(this.factorEncs && { factorEncs: this.factorEncs }),
...(this.chainCode && { chainCode: this.chainCode }),
};
}
}
Expand Down
Loading

0 comments on commit cea9d89

Please sign in to comment.