diff --git a/package-lock.json b/package-lock.json index 8e6191e7..4effc706 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,16 +9,17 @@ "version": "3.2.5", "license": "ISC", "dependencies": { - "@tkey/common-types": "^15.1.0", - "@tkey/core": "^15.1.0", + "@tkey/common-types": "^15.2.1-alpha.0", + "@tkey/core": "^15.2.1-alpha.0", "@tkey/share-serialization": "^15.1.0", "@tkey/storage-layer-torus": "^15.1.0", - "@tkey/tss": "^15.1.0", + "@tkey/tss": "^15.2.1-alpha.0", "@toruslabs/constants": "^14.0.0", "@toruslabs/customauth": "^20.3.0", "@toruslabs/elliptic-wrapper": "^0.1.0", "@toruslabs/fetch-node-details": "^14.0.1", "@toruslabs/fnd-base": "^14.0.0", + "@toruslabs/http-helpers": "^7.0.0", "@toruslabs/metadata-helpers": "^6.0.0", "@toruslabs/openlogin-utils": "^8.2.1", "@toruslabs/session-manager": "^3.1.0", @@ -29,6 +30,7 @@ "bn.js": "^5.2.1", "bowser": "^2.11.0", "elliptic": "^6.5.7", + "hi-base32": "^0.5.1", "loglevel": "^1.9.2" }, "devDependencies": { @@ -3641,10 +3643,9 @@ } }, "node_modules/@tkey/common-types": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@tkey/common-types/-/common-types-15.1.0.tgz", - "integrity": "sha512-oA5gLoyhNNMgCKcjvwLyU31TVS5KMT+lotRrjjoBdDvS0keZwzSLrtHWbXj8jZDlSZaqbd3VlPbCoHcqpk1irA==", - "license": "MIT", + "version": "15.2.1-alpha.0", + "resolved": "https://registry.npmjs.org/@tkey/common-types/-/common-types-15.2.1-alpha.0.tgz", + "integrity": "sha512-0BXtkB2PHNtV+fCmTVhWRqz/0tc975/z2onqCDQyCn0Lk36uhAPbEwCPRJ4vrccICxVizuYagZ+CazaEjk/ICA==", "dependencies": { "@toruslabs/customauth": "^20.3.0", "@toruslabs/eccrypto": "^5.0.4", @@ -3662,12 +3663,11 @@ } }, "node_modules/@tkey/core": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@tkey/core/-/core-15.1.0.tgz", - "integrity": "sha512-JaFprczHR8fBEw1LrwKs87ASgpZagxQ9VZ6lAfAAI8jEh1yhz8djh9l2wzJbaFuLEOQskh7GoxpKfgB+YtBSmw==", - "license": "MIT", + "version": "15.2.1-alpha.0", + "resolved": "https://registry.npmjs.org/@tkey/core/-/core-15.2.1-alpha.0.tgz", + "integrity": "sha512-WHpYdypVrdyv/A5BvHqA/OE2KVBrL1K2dVam39jhjjVlZIwLRiDu/hg2JMym62VVe5I5Jm6UzvTsIAi8YsUZEA==", "dependencies": { - "@tkey/common-types": "^15.1.0", + "@tkey/common-types": "^15.2.1-alpha.0", "@toruslabs/eccrypto": "^5.0.4", "@toruslabs/http-helpers": "^7.0.0", "@toruslabs/torus.js": "^15.1.0", @@ -3685,12 +3685,11 @@ } }, "node_modules/@tkey/service-provider-base": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@tkey/service-provider-base/-/service-provider-base-15.1.0.tgz", - "integrity": "sha512-MruUxiWwyRczZ8KlhhGJ2TQ/p+VFPMOQZ089B5SIi7UsTOBMlzRqJWP3lM2fBSyQsfJCzpzXkj9a29ecpRZe0g==", - "license": "MIT", + "version": "15.2.1-alpha.0", + "resolved": "https://registry.npmjs.org/@tkey/service-provider-base/-/service-provider-base-15.2.1-alpha.0.tgz", + "integrity": "sha512-8CTHuAoglCfDhxk3U0VkWJTl6XcL5UXIQAjtRTo/fbFClsHZ1lfQREXe24m+185XwBsZySrgE+UpL/Qvrq9vow==", "dependencies": { - "@tkey/common-types": "^15.1.0", + "@tkey/common-types": "^15.2.1-alpha.0", "bn.js": "^5.2.1", "elliptic": "^6.5.5" }, @@ -3703,13 +3702,12 @@ } }, "node_modules/@tkey/service-provider-torus": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@tkey/service-provider-torus/-/service-provider-torus-15.1.0.tgz", - "integrity": "sha512-7tA/1ALPo4ToXvwTwMj9OF0wh97S3p1sCeilwRcfyxBMJGpaDW8MSbiAbPqaSkK/DT3AFxlkHwAXWYYZ4+ZueQ==", - "license": "MIT", + "version": "15.2.1-alpha.0", + "resolved": "https://registry.npmjs.org/@tkey/service-provider-torus/-/service-provider-torus-15.2.1-alpha.0.tgz", + "integrity": "sha512-ZkVaZvf0S+wrjN7nVAwq87+X02XJSCal2aCEYoRtoYQAcqp0wgTMPzBB6r4Jo1VyWIYbXS9zl6fZ90h1I86YVQ==", "dependencies": { - "@tkey/common-types": "^15.1.0", - "@tkey/service-provider-base": "^15.1.0", + "@tkey/common-types": "^15.2.1-alpha.0", + "@tkey/service-provider-base": "^15.2.1-alpha.0", "@toruslabs/customauth": "^20.3.0", "@toruslabs/torus.js": "^15.1.0", "bn.js": "^5.2.1", @@ -3741,6 +3739,26 @@ "@babel/runtime": "7.x" } }, + "node_modules/@tkey/share-serialization/node_modules/@tkey/common-types": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/@tkey/common-types/-/common-types-15.1.0.tgz", + "integrity": "sha512-oA5gLoyhNNMgCKcjvwLyU31TVS5KMT+lotRrjjoBdDvS0keZwzSLrtHWbXj8jZDlSZaqbd3VlPbCoHcqpk1irA==", + "dependencies": { + "@toruslabs/customauth": "^20.3.0", + "@toruslabs/eccrypto": "^5.0.4", + "@toruslabs/torus.js": "^15.1.0", + "bn.js": "^5.2.1", + "elliptic": "^6.5.5", + "ts-custom-error": "^3.3.1" + }, + "engines": { + "node": ">=18.x", + "npm": ">=9.x" + }, + "peerDependencies": { + "@babel/runtime": "7.x" + } + }, "node_modules/@tkey/storage-layer-torus": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/@tkey/storage-layer-torus/-/storage-layer-torus-15.1.0.tgz", @@ -3762,16 +3780,36 @@ "@babel/runtime": "7.x" } }, - "node_modules/@tkey/tss": { + "node_modules/@tkey/storage-layer-torus/node_modules/@tkey/common-types": { "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@tkey/tss/-/tss-15.1.0.tgz", - "integrity": "sha512-UcbJbWscIL83Zh1/i6M+X/xhN4EOimGV8JoWZ3D23Ji2pHPo8BgveUZA9i1DK4Y3YqqZ9aS8PvhSHt+KVnNluw==", - "license": "ISC", + "resolved": "https://registry.npmjs.org/@tkey/common-types/-/common-types-15.1.0.tgz", + "integrity": "sha512-oA5gLoyhNNMgCKcjvwLyU31TVS5KMT+lotRrjjoBdDvS0keZwzSLrtHWbXj8jZDlSZaqbd3VlPbCoHcqpk1irA==", "dependencies": { - "@tkey/common-types": "^15.1.0", - "@tkey/core": "^15.1.0", - "@tkey/service-provider-torus": "^15.1.0", "@toruslabs/customauth": "^20.3.0", + "@toruslabs/eccrypto": "^5.0.4", + "@toruslabs/torus.js": "^15.1.0", + "bn.js": "^5.2.1", + "elliptic": "^6.5.5", + "ts-custom-error": "^3.3.1" + }, + "engines": { + "node": ">=18.x", + "npm": ">=9.x" + }, + "peerDependencies": { + "@babel/runtime": "7.x" + } + }, + "node_modules/@tkey/tss": { + "version": "15.2.1-alpha.0", + "resolved": "https://registry.npmjs.org/@tkey/tss/-/tss-15.2.1-alpha.0.tgz", + "integrity": "sha512-DffLXqyZauva1D3fnV6kL/dta47ZyIuPV6EFS0xmkDzGWRd2frow9oZhZJ91XjOfslR/CIdpJ510ISsjcnUaAw==", + "dependencies": { + "@tkey/common-types": "^15.2.1-alpha.0", + "@tkey/core": "^15.2.1-alpha.0", + "@tkey/service-provider-torus": "^15.2.1-alpha.0", + "@toruslabs/customauth": "^20.3.0", + "@toruslabs/http-helpers": "^7.0.0", "@toruslabs/rss-client": "^2.0.1", "@toruslabs/torus.js": "^15.1.0", "@types/bn.js": "^5.1.5", @@ -4232,7 +4270,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/@toruslabs/http-helpers/-/http-helpers-7.0.0.tgz", "integrity": "sha512-U79uCCA1EAManPmgIn+0YpCrKUxj9C7GYlGt7Ftnd3soYCsAXVqWgck+R5knrNvTSOPmot8QYkTl+ncP44Vg/A==", - "license": "MIT", "dependencies": { "deepmerge": "^4.3.1", "loglevel": "^1.9.1" @@ -4303,16 +4340,14 @@ } }, "node_modules/@toruslabs/rss-client": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@toruslabs/rss-client/-/rss-client-2.0.1.tgz", - "integrity": "sha512-EBqAX7LK8ZBy4fiGTGQhI3wVvLvfUyfwREbNkfAxP/iwevgCA2EKLXGWtVsvJj/wXF8HkfjqOcorVeHwP2vvWw==", - "license": "MIT", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@toruslabs/rss-client/-/rss-client-2.0.2.tgz", + "integrity": "sha512-kmtl7KaxTOdrftxtv/hTzz6k2RJbVFq6oPs5Vg4u4iybdQYdRzyP1HqQ3wMu9ADhiItbZAg80VP4gOCCmjao4w==", "dependencies": { "@toruslabs/eccrypto": "^5.0.4", "@toruslabs/http-helpers": "^7.0.0", "bn.js": "^5.2.1", "elliptic": "^6.5.7", - "fetch": "^1.1.0", "loglevel": "^1.9.2" }, "engines": { @@ -5855,18 +5890,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/biskviit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/biskviit/-/biskviit-1.0.1.tgz", - "integrity": "sha512-VGCXdHbdbpEkFgtjkeoBN8vRlbj1ZRX2/mxhE8asCCRalUx2nBzOomLJv8Aw/nRt5+ccDb+tPKidg4XxcfGW4w==", - "license": "MIT", - "dependencies": { - "psl": "^1.1.7" - }, - "engines": { - "node": ">=1.0.0" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -7468,15 +7491,6 @@ "dev": true, "license": "MIT" }, - "node_modules/encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha512-bl1LAgiQc4ZWr++pNYUdRe/alecaHFeHxIJ/pNciqGdKXghaTCOwKkbKp6ye7pKZGu/GcaSXFk8PBVhgs+dJdA==", - "license": "MIT", - "dependencies": { - "iconv-lite": "~0.4.13" - } - }, "node_modules/engine.io-client": { "version": "6.5.4", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", @@ -8713,16 +8727,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-5O8TwrGzoNblBG/jtK4NFuZwNCkZX6s5GfRNOaGtm+QGJEuNakSC/i2RW0R93KX6E0jVjNXm6O3CRN4Ql3K+yA==", - "license": "MIT", - "dependencies": { - "biskviit": "1.0.1", - "encoding": "0.1.12" - } - }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -9656,6 +9660,11 @@ "he": "bin/he" } }, + "node_modules/hi-base32": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz", + "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==" + }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -9760,6 +9769,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -13299,12 +13309,6 @@ "dev": true, "license": "MIT" }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "license": "MIT" - }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -14389,6 +14393,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, "license": "MIT" }, "node_modules/schema-utils": { @@ -15335,7 +15340,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz", "integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==", - "license": "MIT", "engines": { "node": ">=14.0.0" } diff --git a/package.json b/package.json index 7a6cb85f..f9bc4072 100644 --- a/package.json +++ b/package.json @@ -43,19 +43,20 @@ } }, "dependencies": { - "@tkey/common-types": "^15.1.0", - "@tkey/core": "^15.1.0", + "@tkey/common-types": "^15.2.1-alpha.0", + "@tkey/core": "^15.2.1-alpha.0", "@tkey/share-serialization": "^15.1.0", "@tkey/storage-layer-torus": "^15.1.0", - "@tkey/tss": "^15.1.0", + "@tkey/tss": "^15.2.1-alpha.0", "@toruslabs/constants": "^14.0.0", "@toruslabs/customauth": "^20.3.0", "@toruslabs/elliptic-wrapper": "^0.1.0", "@toruslabs/fetch-node-details": "^14.0.1", "@toruslabs/fnd-base": "^14.0.0", + "@toruslabs/http-helpers": "^7.0.0", "@toruslabs/metadata-helpers": "^6.0.0", - "@toruslabs/session-manager": "^3.1.0", "@toruslabs/openlogin-utils": "^8.2.1", + "@toruslabs/session-manager": "^3.1.0", "@toruslabs/torus.js": "^15.1.0", "@toruslabs/tss-client": "^3.1.0", "@toruslabs/tss-frost-client": "0.3.1", @@ -63,6 +64,7 @@ "bn.js": "^5.2.1", "bowser": "^2.11.0", "elliptic": "^6.5.7", + "hi-base32": "^0.5.1", "loglevel": "^1.9.2" }, "devDependencies": { diff --git a/src/helper/browserStorage.ts b/src/helper/browserStorage.ts index 6a5555de..88e48805 100644 --- a/src/helper/browserStorage.ts +++ b/src/helper/browserStorage.ts @@ -21,7 +21,7 @@ export class MemoryStorage implements IStorage { } } -export class AsyncStorage { +export class AsyncStore { public storage: IAsyncStorage | IStorage; private _storeKey: string; @@ -66,3 +66,6 @@ export class AsyncStorage { await this.storage.setItem(this._storeKey, JSON.stringify(store)); } } + +// Deprecated +export class AsyncStorage extends AsyncStore {} diff --git a/src/interfaces.ts b/src/interfaces.ts index b87c23a0..4dbba976 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,5 +1,5 @@ -import { KeyType, Point as TkeyPoint, ShareDescriptionMap } from "@tkey/common-types"; -import { TKeyTSS } from "@tkey/tss"; +import { BNString, FactorEnc, KeyType, Point as TkeyPoint, ShareDescriptionMap } from "@tkey/common-types"; +import { IRemoteClientState, TKeyTSS } from "@tkey/tss"; import type { AGGREGATE_VERIFIER_TYPE, ExtraParams, @@ -9,7 +9,7 @@ import type { TorusVerifierResponse, UX_MODE_TYPE, } from "@toruslabs/customauth"; -import { Client } from "@toruslabs/tss-client"; +import { Client, PointHex } from "@toruslabs/tss-client"; // TODO: move the types to a base class for both dkls and frost in future import type { tssLib as TssDklsLib } from "@toruslabs/tss-dkls-lib"; import type { tssLib as TssFrostLib } from "@toruslabs/tss-frost-lib"; @@ -92,7 +92,7 @@ export interface EnableMFAParams { /** * A BN used for encrypting your Device/ Recovery TSS Key Share. You can generate it using `generateFactorKey()` function or use an existing one. */ - factorKey?: BN; + factorKey?: BNString; /** * Setting the Description of Share - Security Questions, Device Share, Seed Phrase, Password Share, Social Share, Other. Default is Other. */ @@ -100,7 +100,7 @@ export interface EnableMFAParams { /** * Additional metadata information you want to be stored alongside this factor for easy identification. */ - additionalMetadata?: Record; + additionalMetadata?: Record; } export interface CreateFactorParams extends EnableMFAParams { @@ -162,6 +162,7 @@ export interface Web3AuthState { tssPubKey?: Buffer; accountIndex: number; factorKey?: BN; + remoteClient?: IRemoteClientState; } export interface ICoreKit { @@ -449,6 +450,7 @@ export interface SessionData { tssPubKey: string; signatures: string[]; userInfo: UserInfo; + remoteClientState?: IRemoteClientState; } export interface TkeyLocalStoreData { @@ -472,7 +474,42 @@ export interface EthereumSigner { getPublic: () => Promise; } +type SupportedCurve = "secp256k1" | "ed25519"; + +export type ICustomFrostSignParams = { + sessionId: string; + signatures: string[]; + tssCommits: PointHex[]; + factorEnc: FactorEnc; + tssPubKeyHex: string; + curve: SupportedCurve; + + serverXCoords: number[]; + clientXCoord: number; + serverCoefficients: string[]; + clientCoefficient: string; + serverURLs: string[]; +}; + export interface Secp256k1PrecomputedClient { client: Client; serverCoeffs: Record; } + +export interface ICustomDklsSignParams { + sessionId: string; + signatures: string[]; + tssCommits: PointHex[]; + factorEnc: FactorEnc; + tssPubKeyHex: string; + curve: SupportedCurve; + + participatingServerDKGIndexes: number[]; + clientIndex: number; + tssNonce: string; + accountNonce: string; + + endpoints: string[]; + tssWSEndpoints: string[]; + partyIndexes: number[]; +} diff --git a/src/mpcCoreKit.ts b/src/mpcCoreKit.ts index b509543e..84f30fd2 100644 --- a/src/mpcCoreKit.ts +++ b/src/mpcCoreKit.ts @@ -2,7 +2,16 @@ import { BNString, KeyType, Point, secp256k1, SHARE_DELETED, ShareStore, Stringi import { CoreError } from "@tkey/core"; import { ShareSerializationModule } from "@tkey/share-serialization"; import { TorusStorageLayer } from "@tkey/storage-layer-torus"; -import { factorKeyCurve, getPubKeyPoint, lagrangeInterpolation, TKeyTSS, TSSTorusServiceProvider } from "@tkey/tss"; +import { + DELIMITERS, + factorKeyCurve, + getPubKeyPoint, + IRemoteClientState, + lagrangeInterpolation, + pointToHex, + TKeyTSS, + TSSTorusServiceProvider, +} from "@tkey/tss"; import { KEY_TYPE, SIGNER_MAP } from "@toruslabs/constants"; import { AGGREGATE_VERIFIER, TORUS_METHOD, TorusAggregateLoginResponse, TorusLoginResponse, UX_MODE } from "@toruslabs/customauth"; import type { UX_MODE_TYPE } from "@toruslabs/customauth/dist/types/utils/enums"; @@ -38,6 +47,8 @@ import { CreateFactorParams, EnableMFAParams, ICoreKit, + ICustomDklsSignParams, + ICustomFrostSignParams, IFactorKey, InitParams, JWTLoginParams, @@ -51,6 +62,7 @@ import { UserInfo, V3TSSLibType, V4TSSLibType, + WEB3AUTH_NETWORK_TYPE, Web3AuthOptions, Web3AuthOptionsWithDefaults, Web3AuthState, @@ -99,6 +111,10 @@ export class Web3AuthMPCCoreKit implements ICoreKit { private atomicCallStackCounter: number = 0; + private customDklsSign: (params: ICustomDklsSignParams, msgHash: Uint8Array) => Promise<{ v: number; r: Uint8Array; s: Uint8Array }>; + + private customFrostSign: (params: ICustomFrostSignParams, data: Uint8Array) => Promise; + constructor(options: Web3AuthOptions) { if (!options.web3AuthClientId) { throw CoreKitError.clientIdInvalid(); @@ -197,6 +213,16 @@ export class Web3AuthMPCCoreKit implements ICoreKit { return this.keyType === KeyType.ed25519 && this.options.useClientGeneratedTSSKey === undefined ? true : !!this.options.useClientGeneratedTSSKey; } + public setCustomDKLSSign(customDKLSSign: { + sign: (params: ICustomDklsSignParams, msgHash: Uint8Array) => Promise<{ v: number; r: Uint8Array; s: Uint8Array }>; + }) { + this.customDklsSign = customDKLSSign.sign; + } + + public setCustomFrostSign(customFrostSign: { sign: (params: ICustomFrostSignParams, data: Uint8Array) => Promise }) { + this.customFrostSign = customFrostSign.sign; + } + // RecoverTssKey only valid for user that enable MFA where user has 2 type shares : // TssShareType.DEVICE and TssShareType.RECOVERY // if the factors key provided is the same type recovery will not works @@ -473,19 +499,20 @@ export class Web3AuthMPCCoreKit implements ICoreKit { } } - public async inputFactorKey(factorKey: BN): Promise { + public async inputFactorKey(factorKey: BNString): Promise { + const factorKeyBN = new BN(factorKey, "hex"); this.checkReady(); try { // input tkey device share when required share > 0 ( or not reconstructed ) // assumption tkey shares will not changed if (!this.tKey.secp256k1Key) { - const factorKeyMetadata = await this.getFactorKeyMetadata(factorKey); + const factorKeyMetadata = await this.getFactorKeyMetadata(factorKeyBN); await this.tKey.inputShareStoreSafe(factorKeyMetadata, true); } // Finalize initialization. await this.tKey.reconstructKey(); - await this.finalizeTkey(factorKey); + await this.finalizeTkey(factorKeyBN); } catch (err: unknown) { log.error("login error", err); if (err instanceof CoreError) { @@ -590,6 +617,9 @@ export class Web3AuthMPCCoreKit implements ICoreKit { const { shareType } = createFactorParams; let { factorKey, shareDescription, additionalMetadata } = createFactorParams; + if (typeof factorKey === "string") { + factorKey = new BN(factorKey, "hex"); + } if (!VALID_SHARE_INDICES.includes(shareType)) { throw CoreKitError.newShareIndexInvalid(`Invalid share type provided (${shareType}). Valid share types are ${VALID_SHARE_INDICES}.`); @@ -650,17 +680,72 @@ export class Web3AuthMPCCoreKit implements ICoreKit { return ed25519().keyFromPublic(p).getPublic(); } + public async preSetupSigning(): Promise { + const { torusNodeTSSEndpoints } = fetchLocalConfig(this.options.web3AuthNetwork, this.keyType); + + const tssCommits = this.tKey.getTSSCommits(); + + if (!tssCommits[0] || !torusNodeTSSEndpoints) { + throw CoreKitError.tssPublicKeyOrEndpointsMissing(); + } + + const tssNonce = this.getTssNonce() || 0; + + const vid = `${this.verifier}${DELIMITERS.Delimiter1}${this.verifierId}`; + const sessionId = `${vid}${DELIMITERS.Delimiter2}default${DELIMITERS.Delimiter3}${tssNonce}${DELIMITERS.Delimiter4}`; + + const parties = 4; + const clientIndex = parties - 1; + + const { nodeIndexes } = await (this.tKey.serviceProvider as TSSTorusServiceProvider).getTSSPubKey( + this.tKey.tssTag, + this.tKey.metadata.tssNonces[this.tKey.tssTag] + ); + + if (parties - 1 > nodeIndexes.length) { + throw new Error(`Not enough nodes to perform TSS - parties :${parties}, nodeIndexes:${nodeIndexes.length}`); + } + const { + endpoints, + tssWSEndpoints, + partyIndexes, + nodeIndexesReturned: participatingServerDKGIndexes, + } = generateTSSEndpoints(torusNodeTSSEndpoints, parties, clientIndex, nodeIndexes); + + const factor = Point.fromSEC1(secp256k1, this.state.remoteClient.remoteFactorPub); + const factorEnc = this.tKey.getFactorEncs(factor); + + // Compute account nonce only supported for secp256k1 + const accountNonce = this.tkey.computeAccountNonce(this.state.accountIndex); + + return { + endpoints, + tssWSEndpoints, + partyIndexes, + factorEnc, + sessionId, + tssCommits: tssCommits.map((commit) => commit.toPointHex()), + participatingServerDKGIndexes, + clientIndex, + tssNonce: tssNonce.toString(), + accountNonce: accountNonce.toString(), + + signatures: this.state.signatures, + tssPubKeyHex: this.getPubKey().toString("hex"), + curve: this.keyType, + }; + } + public async precompute_secp256k1(): Promise<{ client: Client; serverCoeffs: Record; }> { this.wasmLib = await this.loadTssWasm(); // PreSetup + const { endpoints, tssWSEndpoints, partyIndexes, participatingServerDKGIndexes, clientIndex } = await this.preSetupSigning(); const { tssShareIndex } = this.state; const tssPubKey = this.getPubKeyPoint(); - const { torusNodeTSSEndpoints } = fetchLocalConfig(this.options.web3AuthNetwork, this.keyType); - if (!this.state.factorKey) { throw CoreKitError.factorKeyNotPresent("factorKey not present in state when signing."); } @@ -669,32 +754,18 @@ export class Web3AuthMPCCoreKit implements ICoreKit { }); const tssNonce = this.getTssNonce(); - if (!tssPubKey || !torusNodeTSSEndpoints) { - throw CoreKitError.tssPublicKeyOrEndpointsMissing(); - } - // session is needed for authentication to the web3auth infrastructure holding the factor 1 const randomSessionNonce = generateSessionNonce(); const currentSession = getSessionId(this.verifier, this.verifierId, this.tKey.tssTag, tssNonce, randomSessionNonce); - const parties = 4; - const clientIndex = parties - 1; - // 1. setup - // generate endpoints for servers - const { nodeIndexes } = await this.torusSp.getTSSPubKey(this.tKey.tssTag, this.tKey.metadata.tssNonces[this.tKey.tssTag]); - const { - endpoints, - tssWSEndpoints, - partyIndexes, - nodeIndexesReturned: participatingServerDKGIndexes, - } = generateTSSEndpoints(torusNodeTSSEndpoints, parties, clientIndex, nodeIndexes); - // Setup sockets. const sockets = await setupSockets(tssWSEndpoints, randomSessionNonce); + // Compute account nonce only supported for secp256k1 + const accountNonce = this.tkey.computeAccountNonce(this.state.accountIndex); + const dklsCoeff = getDKLSCoeff(true, participatingServerDKGIndexes, tssShareIndex); const denormalisedShare = dklsCoeff.mul(tssShare).umod(secp256k1.curve.n); - const accountNonce = this.tkey.computeAccountNonce(this.state.accountIndex); const derivedShare = denormalisedShare.add(accountNonce).umod(secp256k1.curve.n); const share = scalarBNToBufferSEC1(derivedShare).toString("base64"); @@ -751,7 +822,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { // mutation function async deleteFactor(factorPub: Point, factorKey?: BNString): Promise { - if (!this.state.factorKey) { + if (!this.state.factorKey && !this.state.remoteClient) { throw CoreKitError.factorKeyNotPresent("factorKey not present in state when deleting a factor."); } if (!this.tKey.metadata.factorPubs) { @@ -828,7 +899,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { public async commitChanges(): Promise { this.checkReady(); - if (!this.state.factorKey) { + if (!this.state.factorKey && !this.state.remoteClient.metadataShare) { throw CoreKitError.factorKeyNotPresent("factorKey not present in state when committing changes."); } @@ -934,10 +1005,52 @@ export class Web3AuthMPCCoreKit implements ICoreKit { } } + async setupRemoteSigning(params: IRemoteClientState, rehydrate: boolean = false): Promise { + const { remoteFactorPub, metadataShare } = params; + const details = this.getKeyDetails().shareDescriptions[remoteFactorPub]; + if (!details) throw CoreKitError.default("factor description not found"); + + const parsedDescription = (details || [])[0] ? JSON.parse(details[0]) : {}; + const { tssShareIndex } = parsedDescription; + + if (!tssShareIndex) throw CoreKitError.default("tss share index not found"); + + const remoteClient: IRemoteClientState = { + remoteFactorPub, + metadataShare, + tssShareIndex, + }; + + const sharestore = ShareStore.fromJSON(JSON.parse(metadataShare)); + await this.tkey.inputShareStoreSafe(sharestore); + await this.tKey.reconstructKey(); + const tssPubKey = this.tKey.getTSSPub().toSEC1(this.tkey.tssCurve, false); + // setup Tkey + // const tssPubKey = Point.fromTkeyPoint(this.tKey.getTSSPub()).toBufferSEC1(false); + this.updateState({ tssShareIndex, tssPubKey, remoteClient }); + // // Finalize setup. + // skip setup provider if rehydrate is true + if (!rehydrate) { + await this.createSessionRemoteClient(); + } + } + public updateState(newState: Partial): void { this.state = { ...this.state, ...newState }; } + public getWeb3AuthNetwork(): WEB3AUTH_NETWORK_TYPE { + return this.options.web3AuthNetwork; + } + + public getMetadataKey(): string { + return this.tkey.secp256k1Key.toString("hex"); + } + + public getMetadataPublicKey(): string { + return this.tkey.getKeyDetails().pubKey.toSEC1(secp256k1, true).toString("hex"); + } + protected async atomicSync(f: () => Promise): Promise { this.atomicCallStackCounter += 1; @@ -950,6 +1063,8 @@ export class Web3AuthMPCCoreKit implements ICoreKit { } } return r; + } catch (e) { + throw e as Error; } finally { this.atomicCallStackCounter -= 1; if (this.atomicCallStackCounter === 0) { @@ -958,6 +1073,31 @@ export class Web3AuthMPCCoreKit implements ICoreKit { } } + private async createSessionRemoteClient() { + try { + const sessionId = SessionManager.generateRandomSessionKey(); + this.sessionManager.sessionId = sessionId; + const { postBoxKey, userInfo, tssShareIndex, tssPubKey } = this.state; + if (!postBoxKey || !tssPubKey || !userInfo) { + throw CoreKitError.userNotLoggedIn(); + } + const payload: SessionData = { + postBoxKey, + factorKey: "", + tssShareIndex: tssShareIndex as number, + tssPubKey: Buffer.from(tssPubKey).toString("hex"), + signatures: this.signatures, + userInfo, + remoteClientState: this.state.remoteClient, + }; + await this.sessionManager.createSession(payload); + // to accommodate async storage + await this.currentStorage.set("sessionId", sessionId); + } catch (err) { + log.error("error creating session", err); + } + } + private async importTssKey(tssKey: string, factorPub: Point, newTSSIndex: TssShareType = TssShareType.DEVICE): Promise { if (!this.state.signatures) { throw CoreKitError.signaturesNotPresent("Signatures not present in state when importing tss key."); @@ -1102,29 +1242,40 @@ export class Web3AuthMPCCoreKit implements ICoreKit { try { this.checkReady(); - const factorKey = new BN(result.factorKey, "hex"); - if (!factorKey) { - throw CoreKitError.providedFactorKeyInvalid(); - } const postBoxKey = result.postBoxKey || result.oAuthKey; if (!postBoxKey) { throw CoreKitError.default("postBoxKey or oAuthKey not present in session data"); } + + const factorKey = new BN(result.factorKey, "hex"); + if (!factorKey && !result.remoteClientState?.metadataShare) { + throw CoreKitError.providedFactorKeyInvalid(); + } + this.torusSp.postboxKey = new BN(postBoxKey, "hex"); this.torusSp.verifierName = result.userInfo.aggregateVerifier || result.userInfo.verifier; this.torusSp.verifierId = result.userInfo.verifierId; - const factorKeyMetadata = await this.getFactorKeyMetadata(factorKey); + await this.tKey.initialize({ neverInitializeNewKey: true }); - await this.tKey.inputShareStoreSafe(factorKeyMetadata, true); + + // skip input share store if factor key is not present + // tkey will be at state initalized + if (!factorKey) { + return; + } + + const metadataShareStore = await this.getFactorKeyMetadata(factorKey); + await this.tKey.inputShareStoreSafe(metadataShareStore, true); await this.tKey.reconstructKey(); this.updateState({ - factorKey: new BN(result.factorKey, "hex"), + factorKey: factorKey ? new BN(result.factorKey, "hex") : undefined, postBoxKey, tssShareIndex: result.tssShareIndex, tssPubKey: this.tkey.getTSSPub().toSEC1(this.tKey.tssCurve, false), signatures: result.signatures, userInfo: result.userInfo, + remoteClient: result.remoteClientState, }); } catch (err) { log.warn("failed to authorize session", err); @@ -1266,7 +1417,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { private async addFactorDescription(args: { factorKey: BN; shareDescription: FactorKeyTypeShareDescription; - additionalMetadata?: Record; + additionalMetadata?: Record; updateMetadata?: boolean; }) { const { factorKey, shareDescription, updateMetadata } = args; @@ -1354,6 +1505,14 @@ export class Web3AuthMPCCoreKit implements ICoreKit { data = keccak256(data); } + // Custom Dkls Sign + if (this.customDklsSign) { + // PreSetup + const setupSigningParams = await this.preSetupSigning(); + const result = await this.customDklsSign(setupSigningParams, data); + return result; + } + const isAlreadyPrecomputed = precomputedTssClient?.client && precomputedTssClient?.serverCoeffs; const { client, serverCoeffs } = isAlreadyPrecomputed ? precomputedTssClient : await this.precompute_secp256k1(); @@ -1408,9 +1567,6 @@ export class Web3AuthMPCCoreKit implements ICoreKit { if (this.state.accountIndex !== 0) { throw CoreKitError.default("Account index not supported for ed25519"); } - const { tssShare } = await this.tKey.getTSSShare(this.state.factorKey); - const clientShareAdjusted = tssShare.mul(clientCoefficient).umod(ec.n); - const clientShareAdjustedHex = ec.scalarToBuffer(clientShareAdjusted, Buffer).toString("hex"); // Generate session identifier. const tssNonce = this.getTssNonce(); @@ -1421,6 +1577,31 @@ export class Web3AuthMPCCoreKit implements ICoreKit { const serverURLs = endpoints.map((x) => x.url); const pubKeyHex = ec.pointToBuffer(tssPubKeyPoint, Buffer).toString("hex"); const serverCoefficientsHex = serverCoefficients.map((c) => ec.scalarToBuffer(c, Buffer).toString("hex")); + + if (this.customFrostSign) { + const factorPub = Point.fromSEC1(secp256k1, this.state.remoteClient.remoteFactorPub); + const params: ICustomFrostSignParams = { + sessionId: session, + signatures: this.signatures, + tssCommits: this.tKey.getTSSCommits().map((commit) => pointToHex(commit)), + factorEnc: this.tKey.getFactorEncs(factorPub), + serverXCoords, + clientXCoord, + serverCoefficients: serverCoefficients.map((sc) => sc.toString("hex")), + clientCoefficient: clientCoefficient.toString("hex"), + tssPubKeyHex: this.getPubKey().toString("hex"), + serverURLs, + curve: this.tkey.tssKeyType, + }; + const result = await this.customFrostSign(params, data); + return Buffer.from(result); + } + + // compute client share + const { tssShare } = await this.tKey.getTSSShare(this.state.factorKey); + const clientShareAdjusted = tssShare.mul(clientCoefficient).umod(ec.n); + const clientShareAdjustedHex = ec.scalarToBuffer(clientShareAdjusted, Buffer).toString("hex"); + const signature = await signEd25519( this.wasmLib as FrostWasmLib, session, diff --git a/tests/factors.spec.ts b/tests/factors.spec.ts index 969a2453..e3a75069 100644 --- a/tests/factors.spec.ts +++ b/tests/factors.spec.ts @@ -7,14 +7,14 @@ import { tssLib as tssLibDKLS } from "@toruslabs/tss-dkls-lib"; import { tssLib as tssLibFROST } from "@toruslabs/tss-frost-lib"; import BN from "bn.js"; -import { COREKIT_STATUS, IAsyncStorage, IStorage, MemoryStorage, TssLib, TssShareType, WEB3AUTH_NETWORK, Web3AuthMPCCoreKit } from "../src"; +import { COREKIT_STATUS, IAsyncStorage, IStorage, MemoryStorage, TssLibType, TssShareType, WEB3AUTH_NETWORK, Web3AuthMPCCoreKit } from "../src"; import { AsyncMemoryStorage, bufferToElliptic, criticalResetAccount, mockLogin } from "./setup"; type FactorTestVariable = { manualSync?: boolean; storage?: IAsyncStorage | IStorage; email: string; - tssLib?: TssLib; + tssLib?: TssLibType; }; function getPubKeys(kit: Web3AuthMPCCoreKit, indices: number[]): EllipticPoint[] { diff --git a/tests/importRecovery.spec.ts b/tests/importRecovery.spec.ts index d7bb18b3..8d3bbc8e 100644 --- a/tests/importRecovery.spec.ts +++ b/tests/importRecovery.spec.ts @@ -4,14 +4,14 @@ import test from "node:test"; import { tssLib as tssLibDKLS } from "@toruslabs/tss-dkls-lib"; import { tssLib as tssLibFROST } from "@toruslabs/tss-frost-lib"; -import { AsyncStorage, MemoryStorage, TssLib, TssShareType, WEB3AUTH_NETWORK } from "../src"; +import { AsyncStorage, MemoryStorage, TssLibType, TssShareType, WEB3AUTH_NETWORK } from "../src"; import { bufferToElliptic, criticalResetAccount, newCoreKitLogInInstance } from "./setup"; type ImportKeyTestVariable = { manualSync?: boolean; email: string; importKeyEmail: string; - tssLib: TssLib; + tssLib: TssLibType; }; const storageInstance = new MemoryStorage();