Skip to content

Commit

Permalink
Merge pull request #6 from tkey/feat/soft-nonce
Browse files Browse the repository at this point in the history
feat: soft nonce implementation
  • Loading branch information
ieow authored Feb 23, 2024
2 parents f232b2d + 8df32af commit b6c02fc
Show file tree
Hide file tree
Showing 21 changed files with 314 additions and 155 deletions.
13 changes: 6 additions & 7 deletions .github/workflows/backward.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ jobs:
steps:
- name: checkout tkey repo
uses: actions/checkout@v3
with:
path: tkey
# with:
# path: tkey

- name: switch path
run: |
cd ./tkey
# - name: switch path
# run: |
# cd ./tkey

- name: Set up node
uses: actions/setup-node@v3
Expand All @@ -54,7 +54,7 @@ jobs:
run: |
cd ./backward-compatibility-tests
npm i
for filename in ../tkey/packages/* ; do
for filename in ../packages/* ; do
echo "installing $filename" || continue
# ... install packed packages
packagename="`ls ${filename}| grep tkey`"
Expand All @@ -63,5 +63,4 @@ jobs:
- name: Running comp tests
run: |
cd ./backward-compatibility-tests
npm test
6 changes: 3 additions & 3 deletions karmaBaseConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ const localBrowserConfig = (webpackConfig, karmaConfig, packageConfig) => {
frameworks: ["mocha", "webpack"],

webpack: {
module: webpackConfig[1].module,
resolve: webpackConfig[1].resolve,
plugins: webpackConfig[1].plugins,
module: webpackConfig[0].module,
resolve: webpackConfig[0].resolve,
plugins: webpackConfig[0].plugins,
},

plugins: ["karma-mocha-reporter", "karma-webkit-launcher", "karma-chrome-launcher", "karma-firefox-launcher", "karma-mocha", "karma-webpack"],
Expand Down
32 changes: 8 additions & 24 deletions package-lock.json

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

1 change: 0 additions & 1 deletion packages/common-types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"ts-custom-error": "^3.3.1"
},
"devDependencies": {
"@types/bn.js": "^5.1.2",
"@types/elliptic": "^6.4.15"
},
"bugs": {
Expand Down
1 change: 1 addition & 0 deletions packages/common-types/src/baseTypes/aggregateTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ export interface ShareRequestArgs {

export type TkeyStoreItemType = {
id: string;
value?: string;
};

export type ISeedPhraseStore = TkeyStoreItemType & {
Expand Down
4 changes: 4 additions & 0 deletions packages/common-types/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,7 @@ export function generateID(): string {
// after the decimal.
return `${Math.random().toString(36).substr(2, 9)}`;
}

export function generateSalt() {
return generatePrivate().toString("hex").padStart(64, "0");
}
1 change: 0 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"json-stable-stringify": "^1.0.2"
},
"devDependencies": {
"@types/bn.js": "^5.1.2",
"@types/elliptic": "^6.4.15",
"@types/json-stable-stringify": "^1.0.34"
},
Expand Down
78 changes: 68 additions & 10 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
FromJSONConstructor,
GenerateNewShareResult,
generatePrivateExcludingIndexes,
generateSalt,
getPubKeyECC,
getPubKeyPoint,
hexPoint,
Expand Down Expand Up @@ -52,6 +53,7 @@ import {
toPrivKeyECC,
} from "@tkey-mpc/common-types";
import { generatePrivate } from "@toruslabs/eccrypto";
import { keccak256 } from "@toruslabs/torus.js";
import BN from "bn.js";
import stringify from "json-stable-stringify";

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

// TODO: handle errors for get and set with retries
export const TSS_MODULE = "tssModule";

class ThresholdKey implements ITKey {
modules: ModuleMap;
Expand Down Expand Up @@ -98,6 +100,8 @@ class ThresholdKey implements ITKey {

_shareSerializationMiddleware: ShareSerializationMiddleware;

_accountSalt: string;

storeDeviceShare: (deviceShareStore: ShareStore, customDeviceInfo?: StringifiedType) => Promise<void>;

haveWriteMetadataLock: string;
Expand Down Expand Up @@ -300,6 +304,11 @@ class ThresholdKey implements ITKey {
});
if (useTSS) {
const { factorEncs, factorPubs, tssPolyCommits } = await this._initializeNewTSSKey(this.tssTag, deviceTSSShare, factorPub, deviceTSSIndex);
const accountSalt = generateSalt();
await this._setTKeyStoreItem(TSS_MODULE, {
id: "accountSalt",
value: accountSalt,
});
this.metadata.addTSSData({ tssTag: this.tssTag, tssNonce: 0, tssPolyCommits, factorPubs, factorEncs });
}
return this.getKeyDetails();
Expand Down Expand Up @@ -385,7 +394,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 +416,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 +426,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.computeAccountNonce(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 +440,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 @@ -446,6 +459,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.computeAccountNonce(accountIndex);
const derivedShare = tssShare.add(nonce).umod(ecCurve.n);
return { tssIndex, tssShare: derivedShare };
}
return { tssIndex, tssShare };
}
}
Expand All @@ -461,8 +479,17 @@ class ThresholdKey implements ITKey {
return tssPolyCommits;
}

getTSSPub(): Point {
return this.getTSSCommits()[0];
getTSSPub(accountIndex?: number): Point {
const tssCommits = this.getTSSCommits();
if (accountIndex && accountIndex > 0) {
const nonce = this.computeAccountNonce(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: tssCommits[0].x.toString("hex"), y: tssCommits[0].y.toString("hex") }).getPublic();
const devicePubKeyPoint = pubKeyPoint.add(noncePub);
return new Point(devicePubKeyPoint.getX().toString("hex"), devicePubKeyPoint.getY().toString("hex"));
}
return tssCommits[0];
}

/**
Expand Down Expand Up @@ -593,6 +620,23 @@ class ThresholdKey implements ITKey {
})
);
}

// only valid for use Tss
// assign account salt from tKey store if it exists
if (Object.keys(this.metadata.tssPolyCommits).length > 0) {
const accountSalt = await this.getTKeyStoreItem(TSS_MODULE, "accountSalt");
if (accountSalt && accountSalt?.value) {
this._accountSalt = accountSalt.value;
} else {
const newSalt = generateSalt();
await this._setTKeyStoreItem(TSS_MODULE, {
id: "accountSalt",
value: newSalt,
});
this._accountSalt = newSalt;
}
}

return { privKey, ...returnObject };
}

Expand Down Expand Up @@ -876,14 +920,18 @@ class ThresholdKey implements ITKey {
serverEncs: refreshResponse.serverFactorEncs,
};
}
const accountSalt = generateSalt();
this.metadata.addTSSData({
tssTag: this.tssTag,
tssNonce: newTssNonce,
tssPolyCommits: newTSSCommits,
factorPubs,
factorEncs,
});
await this._syncShareMetadata();
await this._setTKeyStoreItem(TSS_MODULE, {
id: "accountSalt",
value: accountSalt,
});
} catch (error) {
this.tssTag = oldTag;
throw error;
Expand Down Expand Up @@ -1638,13 +1686,13 @@ class ThresholdKey implements ITKey {

// read errors for what each means
if (latestMetadata.nonce > this.lastFetchedCloudMetadata.nonce) {
throw CoreError.acquireLockFailed(`unable to acquire write access for metadata due to
throw CoreError.acquireLockFailed(`unable to acquire write access for metadata due to
lastFetchedCloudMetadata (${this.lastFetchedCloudMetadata.nonce})
being lower than last written metadata nonce (${latestMetadata.nonce}). perhaps update metadata SDK (create new tKey and init)`);
} else if (latestMetadata.nonce < this.lastFetchedCloudMetadata.nonce) {
throw CoreError.acquireLockFailed(`unable to acquire write access for metadata due to
throw CoreError.acquireLockFailed(`unable to acquire write access for metadata due to
lastFetchedCloudMetadata (${this.lastFetchedCloudMetadata.nonce})
being higher than last written metadata nonce (${latestMetadata.nonce}). this should never happen as it
being higher than last written metadata nonce (${latestMetadata.nonce}). this should never happen as it
should only ever be updated by getting metadata)`);
}

Expand Down Expand Up @@ -1933,6 +1981,16 @@ class ThresholdKey implements ITKey {
this.lastFetchedCloudMetadata = undefined;
}

computeAccountNonce(index: number) {
// generation should occur during tkey.init, fails if accountSalt is absent
if (!this._accountSalt) {
throw CoreError.accountSaltUndefined();
}
let accountHash = keccak256(Buffer.from(`${index}${this._accountSalt}`));
if (accountHash.length === 66) accountHash = accountHash.slice(2);
return index && index > 0 ? new BN(accountHash, "hex").umod(ecCurve.curve.n) : new BN(0);
}

getApi(): ITKeyApi {
return {
getMetadata: this.getMetadata.bind(this),
Expand Down
Loading

0 comments on commit b6c02fc

Please sign in to comment.