-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: soft nonce implementation #6
Changes from 15 commits
cea9d89
454e164
93e01ca
a58413f
01be6c2
970c512
e6d1daa
2b98215
b90f80f
50d9fb3
3d24247
3d88559
3a2f2af
e0f3ba4
312e065
19f276d
a844388
0769f27
99dbec3
11af4f2
0d78a92
ee82873
8df32af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ import { | |
FromJSONConstructor, | ||
GenerateNewShareResult, | ||
generatePrivateExcludingIndexes, | ||
generateSalt, | ||
getPubKeyECC, | ||
getPubKeyPoint, | ||
hexPoint, | ||
|
@@ -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"; | ||
|
||
|
@@ -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; | ||
|
@@ -98,6 +100,8 @@ class ThresholdKey implements ITKey { | |
|
||
_shareSerializationMiddleware: ShareSerializationMiddleware; | ||
|
||
_accountSalt: string; | ||
|
||
storeDeviceShare: (deviceShareStore: ShareStore, customDeviceInfo?: StringifiedType) => Promise<void>; | ||
|
||
haveWriteMetadataLock: string; | ||
|
@@ -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(); | ||
this._setTKeyStoreItem(TSS_MODULE, { | ||
id: "accountSalt", | ||
value: accountSalt, | ||
}); | ||
this.metadata.addTSSData({ tssTag: this.tssTag, tssNonce: 0, tssPolyCommits, factorPubs, factorEncs }); | ||
} | ||
return this.getKeyDetails(); | ||
|
@@ -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); | ||
|
@@ -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(); | ||
|
@@ -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 = await 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..."); | ||
|
@@ -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]; | ||
|
@@ -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 = await this.computeAccountNonce(accountIndex); | ||
const derivedShare = tssShare.add(nonce).umod(ecCurve.n); | ||
return { tssIndex, tssShare: derivedShare }; | ||
} | ||
return { tssIndex, tssShare }; | ||
} | ||
} | ||
|
@@ -461,8 +479,17 @@ class ThresholdKey implements ITKey { | |
return tssPolyCommits; | ||
} | ||
|
||
getTSSPub(): Point { | ||
return this.getTSSCommits()[0]; | ||
async getTSSPub(accountIndex?: number): Promise<Point> { | ||
const tssCommits = this.getTSSCommits(); | ||
if (accountIndex && accountIndex > 0) { | ||
const nonce = await 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 dervicepubKeyPoint = pubKeyPoint.add(noncePub); | ||
return new Point(dervicepubKeyPoint.getX().toString("hex"), dervicepubKeyPoint.getY().toString("hex")); | ||
} | ||
return tssCommits[0]; | ||
} | ||
|
||
/** | ||
|
@@ -876,13 +903,18 @@ class ThresholdKey implements ITKey { | |
serverEncs: refreshResponse.serverFactorEncs, | ||
}; | ||
} | ||
const accountSalt = generateSalt(); | ||
this.metadata.addTSSData({ | ||
tssTag: this.tssTag, | ||
tssNonce: newTssNonce, | ||
tssPolyCommits: newTSSCommits, | ||
factorPubs, | ||
factorEncs, | ||
}); | ||
this._setTKeyStoreItem(TSS_MODULE, { | ||
id: "accountSalt", | ||
value: accountSalt, | ||
}); | ||
await this._syncShareMetadata(); | ||
} catch (error) { | ||
this.tssTag = oldTag; | ||
|
@@ -1968,6 +2000,16 @@ class ThresholdKey implements ITKey { | |
private async initializeModules() { | ||
return Promise.all(Object.keys(this.modules).map((x) => this.modules[x].initialize())); | ||
} | ||
|
||
private async computeAccountNonce(index: number) { | ||
// generation should occur during tkey.init, fails if accountSalt is absent | ||
this._accountSalt = this._accountSalt || (await this.getTKeyStoreItem(TSS_MODULE, "accountSalt")).value; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should getTkeyStore during reconstruct and assign to the accountSalt ( only get from tkey store if the accountSalt is undefined )
|
||
if (!this._accountSalt) { | ||
throw CoreError.accountSaltUndefined(); | ||
} | ||
const accountHash = keccak256(Buffer.from(`${index}${this._accountSalt}`)).slice(2); | ||
return index && index > 0 ? new BN(accountHash, "hex").umod(ecCurve.curve.n) : new BN(0); | ||
} | ||
} | ||
|
||
export default ThresholdKey; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -186,6 +186,7 @@ class Metadata implements IMetadata { | |
tssNonce?: number; | ||
tssPolyCommits?: Point[]; | ||
factorPubs?: Point[]; | ||
accountIndex?: number; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should be removed rite? |
||
factorEncs?: { | ||
[factorPubID: string]: FactorEnc; | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo derivedPubKeyPoint