diff --git a/package-lock.json b/package-lock.json index e1b05fc..5e67e9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,16 +9,17 @@ "version": "4.1.6-alpha.0", "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.2.0", "@toruslabs/customauth": "^20.3.0", "@toruslabs/elliptic-wrapper": "^0.1.1", "@toruslabs/fetch-node-details": "^14.2.0", "@toruslabs/fnd-base": "^14.2.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", @@ -30,6 +31,7 @@ "bn.js": "^5.2.1", "bowser": "^2.11.0", "elliptic": "^6.5.7", + "hi-base32": "^0.5.1", "loglevel": "^1.9.2" }, "devDependencies": { @@ -3754,10 +3756,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", @@ -3799,12 +3800,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", @@ -3846,12 +3846,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" }, @@ -3864,13 +3863,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", @@ -3926,6 +3924,49 @@ "@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/share-serialization/node_modules/@toruslabs/torus.js": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@toruslabs/torus.js/-/torus.js-15.1.1.tgz", + "integrity": "sha512-sLaXA1/R8KTTjU4t+teL3PPaJr2+j01QLYn5IY/t5uTD+1G2nzzfVWpkMDYrk9EfQYw0u4aKJ1lT7j9uKafMlg==", + "dependencies": { + "@toruslabs/bs58": "^1.0.0", + "@toruslabs/constants": "^14.0.0", + "@toruslabs/eccrypto": "^5.0.4", + "@toruslabs/http-helpers": "^7.0.0", + "bn.js": "^5.2.1", + "elliptic": "^6.5.7", + "ethereum-cryptography": "^2.2.1", + "json-stable-stringify": "^1.1.1", + "loglevel": "^1.9.2" + }, + "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", @@ -3947,16 +3988,59 @@ "@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/storage-layer-torus/node_modules/@toruslabs/torus.js": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@toruslabs/torus.js/-/torus.js-15.1.1.tgz", + "integrity": "sha512-sLaXA1/R8KTTjU4t+teL3PPaJr2+j01QLYn5IY/t5uTD+1G2nzzfVWpkMDYrk9EfQYw0u4aKJ1lT7j9uKafMlg==", + "dependencies": { + "@toruslabs/bs58": "^1.0.0", + "@toruslabs/constants": "^14.0.0", + "@toruslabs/eccrypto": "^5.0.4", + "@toruslabs/http-helpers": "^7.0.0", + "bn.js": "^5.2.1", + "elliptic": "^6.5.7", + "ethereum-cryptography": "^2.2.1", + "json-stable-stringify": "^1.1.1", + "loglevel": "^1.9.2" + }, + "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", @@ -4444,7 +4528,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" @@ -5247,7 +5330,6 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/@web3auth/auth/-/auth-9.6.1.tgz", "integrity": "sha512-6iYmjhDtZcXGph3pPTVLShMv/xN+eLnc/+g04rqM34pfb85CK2OsvnufCxWcNpEuS/n5yo5voALPE4s/s2BXWw==", - "license": "MIT", "dependencies": { "@ethereumjs/util": "^9.1.0", "@toruslabs/constants": "^14.2.0", @@ -10081,6 +10163,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", @@ -15764,7 +15851,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 da42e46..36f6275 100644 --- a/package.json +++ b/package.json @@ -47,20 +47,21 @@ } }, "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.2.0", "@toruslabs/customauth": "^20.3.0", "@toruslabs/elliptic-wrapper": "^0.1.1", "@toruslabs/fetch-node-details": "^14.2.0", "@toruslabs/fnd-base": "^14.2.0", + "@toruslabs/http-helpers": "^7.0.0", "@toruslabs/metadata-helpers": "^6.0.0", "@toruslabs/openlogin-utils": "^8.2.1", - "@toruslabs/torus.js": "15.2.0-alpha.0", "@toruslabs/session-manager": "^3.1.0", + "@toruslabs/torus.js": "15.2.0-alpha.0", "@toruslabs/tss-client": "^3.3.0-alpha.0", "@toruslabs/tss-frost-client": "^1.0.1-alpha.0", "@toruslabs/tss-frost-common": "^1.0.2-alpha.0", @@ -68,6 +69,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 6a5555d..88e4880 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/helper/errors.ts b/src/helper/errors.ts index f22c313..5cb262f 100644 --- a/src/helper/errors.ts +++ b/src/helper/errors.ts @@ -339,6 +339,10 @@ class CoreKitError extends AbstractCoreKitError { return CoreKitError.fromCode(1214, extraMessage); } + public static notSupportedForRemoteFactor(extraMessage = ""): ICoreKitError { + return CoreKitError.fromCode(1215, extraMessage); + } + // Initialization and session management public static commitChangesBeforeMFA(extraMessage = ""): ICoreKitError { return CoreKitError.fromCode(1301, extraMessage); diff --git a/src/interfaces.ts b/src/interfaces.ts index ccfb484..b0cb177 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,4 +1,4 @@ -import { KeyType, Point as TkeyPoint, ShareDescriptionMap } from "@tkey/common-types"; +import { BNString, KeyType, Point as TkeyPoint, ShareDescriptionMap } from "@tkey/common-types"; import { TKeyTSS, TSSTorusServiceProvider } from "@tkey/tss"; import { WEB3AUTH_SIG_TYPE } from "@toruslabs/constants"; import type { @@ -22,6 +22,7 @@ import { SafeEventEmitter } from "@web3auth/auth"; import BN from "bn.js"; import { FactorKeyTypeShareDescription, TssShareType, USER_PATH, WEB3AUTH_NETWORK } from "./constants"; +import { IRemoteClientState, IRemoteSignerContext } from "./plugins/ICustomSigner"; import { ISessionSigGenerator } from "./plugins/ISessionSigGenerator"; export type CoreKitMode = UX_MODE_TYPE | "nodejs" | "react-native"; @@ -109,7 +110,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. */ @@ -117,7 +118,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 { @@ -176,7 +177,6 @@ export interface JWTLoginParams { */ prefetchTssPublicKeys?: number; } - export interface Web3AuthState { postBoxKey?: string; signatures?: string[]; @@ -186,6 +186,7 @@ export interface Web3AuthState { tssPubKey?: Buffer; accountIndex: number; factorKey?: BN; + remoteClient?: IRemoteClientState; } export type WEB3AUTH_NETWORK_TYPE = (typeof WEB3AUTH_NETWORK)[keyof typeof WEB3AUTH_NETWORK]; @@ -336,7 +337,7 @@ export interface Web3AuthOptions { } export type Web3AuthOptionsWithDefaults = Required; -export interface IMPCContext { +export interface IMPCContext extends IRemoteSignerContext { stateEmitter: SafeEventEmitter; config: Web3AuthOptionsWithDefaults; status: COREKIT_STATUS; diff --git a/src/mpcCoreKit.ts b/src/mpcCoreKit.ts index 44b7fc1..962eb96 100644 --- a/src/mpcCoreKit.ts +++ b/src/mpcCoreKit.ts @@ -2,7 +2,7 @@ import { BNString, KeyType, ONE_KEY_DELETE_NONCE, Point, secp256k1, SHARE_DELETE 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, lagrangeInterpolation, pointToHex, TKeyTSS, TSSTorusServiceProvider } from "@tkey/tss"; import { 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"; @@ -54,11 +54,13 @@ import { TkeyLocalStoreData, TssLibType, UserInfo, + WEB3AUTH_NETWORK_TYPE, Web3AuthOptions, Web3AuthOptionsWithDefaults, Web3AuthState, } from "./interfaces"; import { DefaultSessionSigGeneratorPlugin } from "./plugins/DefaultSessionSigGenerator"; +import { ICustomDklsSignParams, ICustomFrostSignParams, IRemoteClientState } from "./plugins/ICustomSigner"; import { ISessionSigGenerator } from "./plugins/ISessionSigGenerator"; import { deriveShareCoefficients, @@ -110,6 +112,10 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { private sessionSigGenerator: ISessionSigGenerator; + 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(); @@ -180,7 +186,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { const { tkey } = this; if (!tkey) return COREKIT_STATUS.NOT_INITIALIZED; if (!tkey.metadata) return COREKIT_STATUS.INITIALIZED; - if (!tkey.secp256k1Key || !this.state.factorKey) return COREKIT_STATUS.REQUIRED_SHARE; + if (!tkey.secp256k1Key || !(this.state.factorKey || this.state.remoteClient.remoteFactorPub)) return COREKIT_STATUS.REQUIRED_SHARE; return COREKIT_STATUS.LOGGED_IN; } catch (e) {} return COREKIT_STATUS.NOT_INITIALIZED; @@ -221,6 +227,16 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { return this.sessionSigGenerator.getSessionSigs(); } + 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 @@ -521,19 +537,24 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { } } - public async inputFactorKey(factorKey: BN): Promise { + public async inputFactorKey(factorKey: BNString): Promise { + const factorKeyBN = new BN(factorKey, "hex"); this.checkReady(); + const point = Point.fromScalar(factorKeyBN, secp256k1); + if (!this.getTssFactorPub().includes(point.toSEC1(secp256k1, true).toString("hex"))) { + throw CoreKitError.providedFactorKeyInvalid(); + } 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) { @@ -571,6 +592,13 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { public async enableMFA(enableMFAParams: EnableMFAParams, recoveryFactor = true): Promise { this.checkReady(); + if (!this.state.factorKey) { + if (this.state.remoteClient?.remoteFactorPub) { + throw CoreKitError.notSupportedForRemoteFactor("Cannot enable MFA with remote factor."); + } + throw CoreKitError.factorKeyNotPresent("Current factorKey not present in state when enabling MFA."); + } + const { postBoxKey } = this.state; const hashedFactorKey = getHashedPrivateKey(postBoxKey, this.options.hashedFactorNonce); if (!(await this.checkIfFactorKeyValid(hashedFactorKey))) { @@ -625,9 +653,6 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { public getTssFactorPub = (): string[] => { this.checkReady(); - if (!this.state.factorKey) { - throw CoreKitError.factorKeyNotPresent("factorKey not present in state when getting tss factor public key."); - } const factorPubsList = this.tKey.metadata.factorPubs[this.tKey.tssTag]; return factorPubsList.map((factorPub) => factorPub.toSEC1(factorKeyCurve, true).toString("hex")); }; @@ -635,9 +660,20 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { // mutation function public async createFactor(createFactorParams: CreateFactorParams): Promise { this.checkReady(); + + if (!this.state.factorKey) { + if (this.state.remoteClient?.remoteFactorPub) { + throw CoreKitError.notSupportedForRemoteFactor("Cannot create a factor with remote factor."); + } + throw CoreKitError.factorKeyNotPresent("Current factorKey not present in state when creating a factor."); + } + 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}.`); @@ -716,6 +752,63 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { return p.getX().toBuffer("be", 32); } + 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 = this.state.remoteClient?.remoteFactorPub + ? Point.fromSEC1(secp256k1, this.state.remoteClient?.remoteFactorPub) + : Point.fromScalar(this.state.factorKey, secp256k1); + 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(params?: { sessionSignatures?: string[] }): Promise<{ client: Client; serverCoeffs: Record; @@ -724,11 +817,10 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { const { sessionSignatures } = params || {}; 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."); } @@ -737,32 +829,18 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { }); 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"); @@ -844,6 +922,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { // mutation function async deleteFactor(factorPub: Point, factorKey?: BNString): Promise { if (!this.state.factorKey) { + if (this.state.remoteClient?.remoteFactorPub) throw CoreKitError.notSupportedForRemoteFactor("Cannot delete a remote factor."); throw CoreKitError.factorKeyNotPresent("factorKey not present in state when deleting a factor."); } if (!this.tKey.metadata.factorPubs) { @@ -922,7 +1001,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { 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."); } @@ -1025,6 +1104,48 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { } } + async setupRemoteSigning(params: IRemoteClientState, rehydrate: boolean = false): Promise { + const { remoteFactorPub, metadataShare } = params; + + // rehydrate session + if (rehydrate) { + this.updateState({ remoteClient: params }); + const sessionResult = await this.sessionManager.authorizeSession().catch(async (err) => { + log.error("rehydrate session error", err); + }); + if (sessionResult) { + await this.rehydrateSession(sessionResult); + } + } + + 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 }; } @@ -1085,6 +1206,18 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { } } + public getWeb3AuthNetwork(): WEB3AUTH_NETWORK_TYPE { + return this.options.web3AuthNetwork; + } + + public getMetadataKey(): string { + return this.tkey.secp256k1Key.toString("hex").padStart(64, "0"); + } + + public getMetadataPublicKey(): string { + return this.tkey.getKeyDetails().pubKey.toSEC1(secp256k1, true).toString("hex"); + } + protected async atomicSync(f: () => Promise): Promise { this.atomicCallStackCounter += 1; @@ -1107,6 +1240,30 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { } } + 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: await this.getSessionSignatures(), + userInfo, + }; + 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."); @@ -1186,22 +1343,25 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { } const hashedFactorKey = getHashedPrivateKey(this.state.postBoxKey, this.options.hashedFactorNonce); - this.state.factorKey = hashedFactorKey; - if (await this.checkIfFactorKeyValid(hashedFactorKey)) { - // Initialize tkey with existing hashed share if available. - const factorKeyMetadata: ShareStore = await this.getFactorKeyMetadata(hashedFactorKey); - try { - await this.tKey.inputShareStoreSafe(factorKeyMetadata, true); - await this.tKey.reconstructKey(); - await this.finalizeTkey(hashedFactorKey); - } catch (err) { - log.error("error initializing tkey with hashed share", err); - } - } else { - const factorKeyMetadata = await this.tKey?.readMetadata(hashedFactorKey); - if (factorKeyMetadata.message === "SHARE_DELETED") { - // throw CoreKitError.hashedFactorDeleted(); - log.warn("hashed factor deleted"); + const point = Point.fromScalar(hashedFactorKey, secp256k1); + if (this.getTssFactorPub().includes(point.toSEC1(secp256k1, true).toString("hex"))) { + this.state.factorKey = hashedFactorKey; + if (await this.checkIfFactorKeyValid(hashedFactorKey)) { + // Initialize tkey with existing hashed share if available. + const factorKeyMetadata: ShareStore = await this.getFactorKeyMetadata(hashedFactorKey); + try { + await this.tKey.inputShareStoreSafe(factorKeyMetadata, true); + await this.tKey.reconstructKey(); + await this.finalizeTkey(hashedFactorKey); + } catch (err) { + log.error("error initializing tkey with hashed share", err); + } + } else { + const factorKeyMetadata = await this.tKey?.readMetadata(hashedFactorKey); + if (factorKeyMetadata.message === "SHARE_DELETED") { + // throw CoreKitError.hashedFactorDeleted(); + log.warn("hashed factor deleted"); + } } } } @@ -1230,24 +1390,31 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { 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 (!result.factorKey && !this.state.remoteClient.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); + + const metadataShareStore = this.state.remoteClient?.metadataShare + ? ShareStore.fromJSON(JSON.parse(this.state.remoteClient.metadataShare)) + : 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, postboxKeyNodeIndexes: result.postboxKeyNodeIndexes || [], tssShareIndex: result.tssShareIndex, @@ -1400,7 +1567,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { private async addFactorDescription(args: { factorKey: BN; shareDescription: FactorKeyTypeShareDescription; - additionalMetadata?: Record; + additionalMetadata?: Record; updateMetadata?: boolean; }) { const { factorKey, shareDescription, updateMetadata } = args; @@ -1488,6 +1655,14 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { 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, signatures } = isAlreadyPrecomputed ? precomputedTssClient : await this.precompute_secp256k1(); @@ -1544,9 +1719,6 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { if (this._sigType === "ed25519" && 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(); @@ -1558,6 +1730,31 @@ export class Web3AuthMPCCoreKit implements ICoreKit, IMPCContext { const pubKeyHex = ec.pointToBuffer(tssPubKeyPoint, Buffer).toString("hex"); const serverCoefficientsHex = serverCoefficients.map((c) => ec.scalarToBuffer(c, Buffer).toString("hex")); const authSignatures = await this.getSessionSignatures(); + + if (this.customFrostSign) { + const factorPub = Point.fromSEC1(secp256k1, this.state.remoteClient.remoteFactorPub); + const params: ICustomFrostSignParams = { + sessionId: session, + signatures: await this.getSessionSignatures(), + 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 signFrost( this.wasmLib as FrostWasmLibEd25519 | FrostWasmLibBip340, session, diff --git a/src/plugins/ICustomSigner.ts b/src/plugins/ICustomSigner.ts new file mode 100644 index 0000000..75097fb --- /dev/null +++ b/src/plugins/ICustomSigner.ts @@ -0,0 +1,71 @@ +import { FactorEnc, Point, ShareDescriptionMap } from "@tkey/common-types"; +import { PointHex } from "@toruslabs/tss-client"; +import { SafeEventEmitter } from "@web3auth/auth"; + +import { COREKIT_STATUS, CreateFactorParams, WEB3AUTH_NETWORK_TYPE } from "../interfaces"; + +export 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 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[]; +} + +export interface ICustomDKLSSign { + sign: (params: ICustomDklsSignParams, msgHash: Uint8Array) => Promise<{ v: number; r: Uint8Array; s: Uint8Array }>; +} + +export interface ICustomFrostSign { + sign: (params: ICustomFrostSignParams, msgHash: Uint8Array) => Promise; +} + +export interface IRemoteClientState { + remoteFactorPub: string; + metadataShare: string; + tssShareIndex: number; +} + +export interface IRemoteSignerContext { + status: COREKIT_STATUS; + stateEmitter: SafeEventEmitter; + setupRemoteSigning(params: Omit, rehydrate?: boolean): Promise; + createFactor(createFactorParams: CreateFactorParams): Promise; + inputFactorKey(factorKey: string): Promise; + deleteFactor(factorPub: Point, factorKey?: string): Promise; + getKeyDetails(): Record & { shareDescriptions: ShareDescriptionMap }; + getMetadataKey(): string | undefined; + getMetadataPublicKey(): string | undefined; + getWeb3AuthNetwork(): WEB3AUTH_NETWORK_TYPE; + + // Added new methods + setCustomDKLSSign(customDKLSSign: ICustomDKLSSign): void; + setCustomFrostSign(customDKLSSign: ICustomFrostSign): void; +} diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 789c86a..62bef67 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,2 +1,3 @@ export * from "./DefaultSessionSigGenerator"; +export * from "./ICustomSigner"; export * from "./ISessionSigGenerator";