diff --git a/src/helper/browserStorage.ts b/src/helper/browserStorage.ts index 6a5555de..e5765727 100644 --- a/src/helper/browserStorage.ts +++ b/src/helper/browserStorage.ts @@ -1,4 +1,8 @@ -import { IAsyncStorage, IStorage } from "../interfaces"; +import { TKeyTSS } from "@tkey/tss"; +import BN from "bn.js"; + +import { FIELD_ELEMENT_HEX_LEN } from "../constants"; +import { IAsyncStorage, IStorage, TkeyLocalStoreData } from "../interfaces"; import CoreKitError from "./errors"; export class MemoryStorage implements IStorage { @@ -66,3 +70,42 @@ export class AsyncStorage { await this.storage.setItem(this._storeKey, JSON.stringify(store)); } } + +export class DeviceStorage { + private tKey: TKeyTSS; + + private currentStorage: AsyncStorage; + + constructor(tkeyInstance: TKeyTSS, currentStorage: AsyncStorage) { + this.tKey = tkeyInstance; + this.currentStorage = currentStorage; + } + + // device factor + async setDeviceFactor(factorKey: BN, replace = false): Promise { + if (!replace) { + const existingFactor = await this.getDeviceFactor(); + if (existingFactor) { + throw CoreKitError.default("Device factor already exists"); + } + } + + const metadata = this.tKey.getMetadata(); + const tkeyPubX = metadata.pubKey.x.toString(16, FIELD_ELEMENT_HEX_LEN); + await this.currentStorage.set( + tkeyPubX, + JSON.stringify({ + factorKey: factorKey.toString("hex").padStart(64, "0"), + } as TkeyLocalStoreData) + ); + } + + async getDeviceFactor(): Promise { + const metadata = this.tKey.getMetadata(); + + const tkeyPubX = metadata.pubKey.x.toString(16, FIELD_ELEMENT_HEX_LEN); + const tKeyLocalStoreString = await this.currentStorage.get(tkeyPubX); + const tKeyLocalStore = JSON.parse(tKeyLocalStoreString || "{}") as TkeyLocalStoreData; + return tKeyLocalStore.factorKey; + } +} diff --git a/src/index.ts b/src/index.ts index 3e41424a..5aae2b12 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,5 +2,6 @@ export * from "./constants"; export * from "./helper"; export * from "./interfaces"; export * from "./mpcCoreKit"; +export * from "./remoteFactorServices"; export * from "./utils"; export { factorKeyCurve } from "@tkey/tss"; diff --git a/src/interfaces.ts b/src/interfaces.ts index b087a675..c438e88c 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -15,7 +15,7 @@ import type { tssLib as TssFrostLib } from "@toruslabs/tss-frost-lib"; import BN from "bn.js"; import { FactorKeyTypeShareDescription, TssShareType, USER_PATH, WEB3AUTH_NETWORK } from "./constants"; -import { IRemoteClientState } from "./remoteSignInterfaces"; +import { IRemoteClientState } from "./remoteFactorServices/remoteSignInterfaces"; export type CoreKitMode = UX_MODE_TYPE | "nodejs" | "react-native"; @@ -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 { @@ -443,6 +443,7 @@ export interface SessionData { tssPubKey: string; signatures: string[]; userInfo: UserInfo; + remoteClientState?: IRemoteClientState; } export interface TkeyLocalStoreData { diff --git a/src/mpcCoreKit.ts b/src/mpcCoreKit.ts index 73ce86d9..57f1ab24 100644 --- a/src/mpcCoreKit.ts +++ b/src/mpcCoreKit.ts @@ -56,7 +56,7 @@ import { Web3AuthOptionsWithDefaults, Web3AuthState, } from "./interfaces"; -import { IRemoteClientState, RefreshRemoteTssReturnType } from "./remoteSignInterfaces"; +import { IRemoteClientState, RefreshRemoteTssReturnType } from "./remoteFactorServices/remoteSignInterfaces"; import { deriveShareCoefficients, ed25519, @@ -617,7 +617,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { } return this.atomicSync(async () => { - if (this.state.remoteClient && !this.state.factorKey) { + if (this.state.remoteClient) { if (shareType === this.state.tssShareIndex) { await this.remoteCopyFactorPub(factorPub, shareType); } else { @@ -627,7 +627,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { await this.copyOrCreateShare(shareType, factorPub); } await this.backupMetadataShare(factorKey); - await this.addFactorDescription(factorKey, shareDescription, additionalMetadata); + await this.addFactorDescription(factorKey, shareDescription, additionalMetadata, false); return scalarBNToBufferSEC1(factorKey).toString("hex"); }).catch((reason: Error) => { @@ -667,7 +667,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { public async sign(data: Buffer, hashed: boolean = false): Promise { this.wasmLib = await this.loadTssWasm(); if (this.keyType === KeyType.secp256k1) { - if (this.state.remoteClient && !this.state.factorKey) { + if (this.state.remoteClient) { const sig = await this.remoteSignSecp256k1(data, hashed); return Buffer.concat([sig.r, sig.s, Buffer.from([sig.v])]); } @@ -699,7 +699,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { throw CoreKitError.factorInUseCannotBeDeleted("Cannot delete current active factor"); } - if (this.state.remoteClient && !this.state.factorKey) { + if (this.state.remoteClient) { await this.remoteDeleteFactorPub(factorPub); } else { await this.tKey.deleteFactorPub({ factorKey: this.state.factorKey, deleteFactorPub: factorPub, authSignatures: this.signatures }); @@ -868,8 +868,15 @@ export class Web3AuthMPCCoreKit implements ICoreKit { } } - async setupRemoteSigning(params: IRemoteClientState): Promise> { - const { remoteClientUrl, remoteFactorPub, metadataShare, remoteClientToken, tssShareIndex } = params; + async setupRemoteSigning(params: Omit): Promise { + const { remoteClientUrl, remoteFactorPub, metadataShare, remoteClientToken } = 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 = { remoteClientUrl: remoteClientUrl.at(-1) === "/" ? remoteClientUrl.slice(0, -1) : remoteClientUrl, @@ -880,16 +887,15 @@ export class Web3AuthMPCCoreKit implements ICoreKit { }; const sharestore = ShareStore.fromJSON(JSON.parse(metadataShare)); - this.tkey.inputShareStoreSafe(sharestore); + 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. // setup provider - await this.createSession(); + await this.createSessionRemoteClient(); } /** @@ -942,7 +948,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { const result = ( await post<{ data: RefreshRemoteTssReturnType }>( - `${remoteClient.remoteClientUrl}/api/mpc/refresh_tss`, + `${remoteClient.remoteClientUrl}/api/v3/mpc/refresh_tss`, { dataRequired }, { headers: { @@ -973,7 +979,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { const result = ( await post<{ data?: EncryptedMessage }>( - `${this.state.remoteClient.remoteClientUrl}/api/mpc/copy_tss_share`, + `${this.state.remoteClient.remoteClientUrl}/api/v3/mpc/copy_tss_share`, { dataRequired }, { headers: { @@ -1084,7 +1090,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { msgHash: msgData.toString("hex"), }; - const result = await post<{ data?: Record }>(`${this.state.remoteClient.remoteClientUrl}/api/mpc/sign`, data, { + const result = await post<{ data?: Record }>(`${this.state.remoteClient.remoteClientUrl}/api/v3/mpc/sign`, data, { headers: { Authorization: `Bearer ${this.state.remoteClient.remoteClientToken}`, }, @@ -1104,6 +1110,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."); @@ -1182,9 +1213,9 @@ export class Web3AuthMPCCoreKit implements ICoreKit { // Store factor description. await this.backupMetadataShare(factorKey); if (this.options.disableHashedFactorKey) { - await this.addFactorDescription(factorKey, FactorKeyTypeShareDescription.Other); + await this.addFactorDescription(factorKey, FactorKeyTypeShareDescription.Other, undefined, false); } else { - await this.addFactorDescription(factorKey, FactorKeyTypeShareDescription.HashedShare); + await this.addFactorDescription(factorKey, FactorKeyTypeShareDescription.HashedShare, undefined, false); } }); } @@ -1400,7 +1431,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { private async addFactorDescription( factorKey: BN, shareDescription: FactorKeyTypeShareDescription, - additionalMetadata: Record = {}, + additionalMetadata: Record = {}, updateMetadata = true ) { const { tssIndex } = await this.tKey.getTSSShare(factorKey); diff --git a/src/remoteFactorServices/authenticator.ts b/src/remoteFactorServices/authenticator.ts new file mode 100644 index 00000000..257aa1c5 --- /dev/null +++ b/src/remoteFactorServices/authenticator.ts @@ -0,0 +1,121 @@ +import { Point, secp256k1 } from "@tkey/common-types"; +import { generatePrivateBN } from "@tkey/core"; +import { post } from "@toruslabs/http-helpers"; +import { keccak256 } from "@toruslabs/metadata-helpers"; +import BN from "bn.js"; +import base32 from "hi-base32"; + +import { WEB3AUTH_NETWORK_TYPE, Web3AuthMPCCoreKit } from "../"; +import { IRemoteClientState } from "./remoteSignInterfaces"; + +// todo, to replace with ICorekit +export function registerAuthenticatorService(_params: { backendUrl: string; secret: string; corekitInstance: Web3AuthMPCCoreKit }) { + // corekitInstance create factor + // sign secret with the corekit + // AuthenticatorService register +} + +// generate secret key for authenticator +export function generateSecretKey(): string { + const key = generatePrivateBN().toArray().slice(0, 20); + return base32.encode(key).toString().replace(/=/g, ""); +} + +export class AuthenticatorService { + public factorKey?: BN; + + // publickey + public factorPub?: string; + + private backendUrl: string; + + private web3authNetwork: WEB3AUTH_NETWORK_TYPE; + + private metadataUrl?: string; + + constructor(params: { backendUrl: string; web3authNetwork: WEB3AUTH_NETWORK_TYPE; overrideMetadataUrl?: string }) { + const { backendUrl } = params; + this.backendUrl = backendUrl; + this.web3authNetwork = params.web3authNetwork; + this.metadataUrl = params.overrideMetadataUrl; + } + + async register(secretKey: string, factorKey: string): Promise<{ success: boolean; message?: string }> { + // get pubkey + const privKeyPair = secp256k1.keyFromPrivate(factorKey); + const pubKey = privKeyPair.getPublic(); + const point = Point.fromElliptic(pubKey); + // sign secret + const sig = secp256k1.sign(keccak256(Buffer.from(secretKey, "utf8")), privKeyPair.getPrivate().toBuffer()); + + const data = { + address: point.toSEC1(secp256k1, true).toString("hex"), + sig: { + r: sig.r.toString("hex").padStart(64, "0"), + s: sig.s.toString("hex").padStart(64, "0"), + v: new BN(sig.recoveryParam).toString(16, 2), + }, + secretKey, + }; + + const resp = await post<{ + success: boolean; + message: string; + }>(`${this.backendUrl}/api/v3/register`, data); + + this.factorKey = new BN(factorKey, "hex"); + this.factorPub = point.toSEC1(secp256k1, true).toString("hex"); + + return resp; + } + + async verifyRegistration(code: string) { + if (!this.factorKey) throw new Error("factorKey is not defined"); + if (!this.factorPub) throw new Error("address is not defined"); + if (!code) throw new Error("code is not defined"); + + const data = { + address: this.factorPub.replaceAll("0x", ""), + code, + data: { + // If the verification is complete, we save the factorKey for the user address. + // This factorKey is used to verify the user in the future on a new device and recover tss share. + factorKey: this.factorKey.toString(16, 64), + }, + }; + + await post(`${this.backendUrl}/api/v3/verify`, data); + } + + // Verify the mfa code and return the factorKey + async verifyAuthenticatorRecovery(factorPub: string, code: string): Promise { + const verificationData = { + address: factorPub, + code, + }; + + const response = await post<{ data?: Record }>(`${this.backendUrl}/api/v3/verify`, verificationData); + const { data } = response; + return data ? new BN(data.factorKey, "hex") : undefined; + } + + // verify the mfa code and return the remote client setyp params + async verifyRemoteSetup(factorPub: string, code: string): Promise> { + const verificationData = { + address: factorPub, + code, + web3authNetwork: this.web3authNetwork, + metadataUrl: this.metadataUrl, + }; + + const response = await post<{ data?: Record }>(`${this.backendUrl}/api/v3/verify_remote`, verificationData); + const { data } = response; + + return { + remoteClientUrl: this.backendUrl, + remoteFactorPub: data.factorPub, + metadataShare: data.metadataShare, + remoteClientToken: data.signature, + }; + } +} diff --git a/src/remoteFactorServices/index.ts b/src/remoteFactorServices/index.ts new file mode 100644 index 00000000..711c83b8 --- /dev/null +++ b/src/remoteFactorServices/index.ts @@ -0,0 +1,23 @@ +import { ShareDescriptionMap } from "@tkey/common-types"; +import log from "loglevel"; + +import { RemoteFactorType } from "./remoteSignInterfaces"; + +export * from "./authenticator"; +export * from "./remoteSignInterfaces"; +export * from "./smsOtp"; + +export function getFactorDetailsAndDescriptions(shareDescriptions: ShareDescriptionMap, factorType: RemoteFactorType) { + const arrayOfDescriptions = Object.entries(shareDescriptions).map(([key, value]) => { + const parsedDescription = (value || [])[0] ? JSON.parse(value[0]) : {}; + return { + key, + description: parsedDescription, + }; + }); + + const shareDescriptionsMobile = arrayOfDescriptions.find(({ description }) => description.authenticator === factorType); + log.info("shareDescriptionsMobile", shareDescriptionsMobile); + + return { shareDescriptionsMobile, factorPub: shareDescriptionsMobile?.key, tssIndex: shareDescriptionsMobile?.description.tssShareIndex }; +} diff --git a/src/remoteSignInterfaces.ts b/src/remoteFactorServices/remoteSignInterfaces.ts similarity index 73% rename from src/remoteSignInterfaces.ts rename to src/remoteFactorServices/remoteSignInterfaces.ts index cc02c263..081c240a 100644 --- a/src/remoteSignInterfaces.ts +++ b/src/remoteFactorServices/remoteSignInterfaces.ts @@ -1,6 +1,20 @@ import { FactorEnc, Point } from "@tkey/common-types"; import { PointHex } from "@toruslabs/tss-client"; +import { FactorKeyTypeShareDescription, TssShareType } from "../constants"; + +export enum RemoteFactorType { + SMS = "sms", + Authenticator = "authenticator", +} + +export interface RemoteFactorDescription { + module: FactorKeyTypeShareDescription; + tssShareIndex: TssShareType; + authenticator: RemoteFactorType; + description: string; +} + export interface IRemoteClientState { remoteFactorPub: string; remoteClientUrl: string; @@ -31,6 +45,7 @@ export interface refreshRemoteTssType { authSignatures: string[]; }; } + export interface RefreshRemoteTssReturnType { tssTag: string; tssNonce: number; diff --git a/src/remoteSignerServices/smsOtp.ts b/src/remoteFactorServices/smsOtp.ts similarity index 62% rename from src/remoteSignerServices/smsOtp.ts rename to src/remoteFactorServices/smsOtp.ts index 7867606d..231e4c2e 100644 --- a/src/remoteSignerServices/smsOtp.ts +++ b/src/remoteFactorServices/smsOtp.ts @@ -1,48 +1,17 @@ -import { secp256k1, ShareDescriptionMap } from "@tkey/common-types"; +import { secp256k1 } from "@tkey/common-types"; import { post } from "@toruslabs/http-helpers"; import { keccak256 } from "@toruslabs/metadata-helpers"; import BN from "bn.js"; import type { ec } from "elliptic"; -import log from "loglevel"; -import { IRemoteClientState } from "src/remoteSignInterfaces"; + +import { IRemoteClientState } from "./remoteSignInterfaces"; export class SmsService { private backendUrl: string; - private shareDescriptions: ShareDescriptionMap; - - private authenticatorType: string = "sms"; - - private factorPub: string = ""; - - private tssIndex: number; - - constructor(params: { backendUrl: string; shareDescriptions: ShareDescriptionMap; authenticatorType?: string }) { + constructor(params: { backendUrl: string }) { const { backendUrl } = params; this.backendUrl = backendUrl; - this.authenticatorType = params.authenticatorType || "sms"; - this.shareDescriptions = params.shareDescriptions; - this.getDescriptionsAndUpdate(); - } - - getDescriptionsAndUpdate() { - const arrayOfDescriptions = Object.entries(this.shareDescriptions).map(([key, value]) => { - const parsedDescription = (value || [])[0] ? JSON.parse(value[0]) : {}; - return { - key, - description: parsedDescription, - }; - }); - - const shareDescriptionsMobile = arrayOfDescriptions.find(({ description }) => description.authenticator === this.authenticatorType); - log.info("shareDescriptionsMobile", shareDescriptionsMobile); - - if (shareDescriptionsMobile) { - this.factorPub = shareDescriptionsMobile.key; - this.tssIndex = shareDescriptionsMobile.description.tssShareIndex; - } - - return shareDescriptionsMobile; } async registerSmsOTP(privKey: BN, number: string): Promise { @@ -67,7 +36,7 @@ export class SmsService { success: boolean; id_token?: string; message: string; - }>(`${this.backendUrl}/api/v1/register`, data); + }>(`${this.backendUrl}/api/v3/register`, data); // this is to send sms to the user instantly after registration. const startData = { @@ -75,7 +44,7 @@ export class SmsService { }; // Sends the user sms. - const resp2 = await post<{ success: boolean; code?: string }>(`${this.backendUrl}/api/v1/start`, startData); + const resp2 = await post<{ success: boolean; code?: string }>(`${this.backendUrl}/api/v3/start`, startData); // if (resp2.status !== 200) throw new Error("Error sending sms"); return resp2.code; } @@ -94,14 +63,14 @@ export class SmsService { }, }; - await post(`${this.backendUrl}/api/v1/verify`, data); + await post(`${this.backendUrl}/api/v3/verify`, data); } async requestSMSOTP(address: string): Promise { const startData = { address, }; - const resp2 = await post<{ success?: boolean; code?: string }>(`${this.backendUrl}/api/v1/start`, startData); + const resp2 = await post<{ success?: boolean; code?: string }>(`${this.backendUrl}/api/v3/start`, startData); // eslint-disable-next-line no-console console.log(resp2); return resp2.code; @@ -113,24 +82,23 @@ export class SmsService { code, }; - const response = await post<{ data?: Record }>(`${this.backendUrl}/api/v1/verify`, verificationData); + const response = await post<{ data?: Record }>(`${this.backendUrl}/api/v3/verify`, verificationData); const { data } = response; return data ? new BN(data.factorKey, "hex") : undefined; } - async verifyRemoteSetup(address: string, code: string): Promise { + async verifyRemoteSetup(address: string, code: string): Promise> { const verificationData = { address, code, }; - const response = await post<{ data?: Record }>(`${this.backendUrl}/api/v1/verify_remote`, verificationData); + const response = await post<{ data?: Record }>(`${this.backendUrl}/api/v3/verify_remote`, verificationData); const { data } = response; return { - tssShareIndex: this.tssIndex, remoteClientUrl: this.backendUrl, - remoteFactorPub: this.factorPub, + remoteFactorPub: data.factorPub, metadataShare: data.metadataShare, remoteClientToken: data.signature, }; diff --git a/src/remoteSignerServices/authenticator.ts b/src/remoteSignerServices/authenticator.ts deleted file mode 100644 index 40fa32b5..00000000 --- a/src/remoteSignerServices/authenticator.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { secp256k1, ShareDescriptionMap } from "@tkey/common-types"; -import { generatePrivateBN } from "@tkey/core"; -import { post } from "@toruslabs/http-helpers"; -import { keccak256 } from "@toruslabs/metadata-helpers"; -import BN from "bn.js"; -import type { ec } from "elliptic"; -import base32 from "hi-base32"; -import log from "loglevel"; -import { IRemoteClientState } from "src/remoteSignInterfaces"; - -export class AuthenticatorService { - private backendUrl: string; - - private shareDescriptions: ShareDescriptionMap; - - private authenticatorType: string = "authenticator"; - - private factorPub: string = ""; - - private tssIndex: number; - - constructor(params: { backendUrl: string; shareDescriptions: ShareDescriptionMap; authenticatorType?: string }) { - const { backendUrl } = params; - this.backendUrl = backendUrl; - this.authenticatorType = params.authenticatorType || "authenticator"; - this.shareDescriptions = params.shareDescriptions; - // this.remoteClient = remoteClient || false; - } - - getDescriptionsAndUpdate() { - const arrayOfDescriptions = Object.entries(this.shareDescriptions).map(([key, value]) => { - const parsedDescription = (value || [])[0] ? JSON.parse(value[0]) : {}; - return { - key, - description: parsedDescription, - }; - }); - - const shareDescriptionsMobile = arrayOfDescriptions.find(({ description }) => description.authenticator === this.authenticatorType); - log.info("shareDescriptionsMobile", shareDescriptionsMobile); - - if (shareDescriptionsMobile) { - this.factorPub = shareDescriptionsMobile.key; - this.tssIndex = shareDescriptionsMobile.description.tssShareIndex; - } - - return shareDescriptionsMobile; - } - - generateSecretKey(): string { - const key = generatePrivateBN().toArray().slice(0, 20); - return base32.encode(key).toString().replace(/=/g, ""); - } - - async register(privKey: BN, secretKey: string): Promise<{ success: boolean; message?: string }> { - const privKeyPair: ec.KeyPair = secp256k1.keyFromPrivate(privKey.toString(16, 64)); - const pubKey = privKeyPair.getPublic(); - const sig = secp256k1.sign(keccak256(Buffer.from(secretKey, "utf8")), Buffer.from(privKey.toString(16, 64), "hex")); - - const data = { - pubKey: { - x: pubKey.getX().toString(16, 64), - y: pubKey.getY().toString(16, 64), - }, - sig: { - r: sig.r.toString(16, 64), - s: sig.s.toString(16, 64), - v: new BN(sig.recoveryParam as number).toString(16, 2), - }, - secretKey, - }; - - const resp = await post<{ - success: boolean; - message: string; - }>(`${this.backendUrl}/api/v1/register`, data); - - return resp; - } - - async addAuthenticatorRecovery(address: string, code: string, factorKey: BN) { - if (!factorKey) throw new Error("factorKey is not defined"); - if (!address) throw new Error("address is not defined"); - if (!code) throw new Error("code is not defined"); - - const data = { - address, - code, - data: { - // If the verification is complete, we save the factorKey for the user address. - // This factorKey is used to verify the user in the future on a new device and recover tss share. - factorKey: factorKey.toString(16, 64), - }, - }; - - await post(`${this.backendUrl}/api/v1/verify`, data); - } - - async verifyAuthenticatorRecovery(address: string, code: string): Promise { - const verificationData = { - address, - code, - }; - - const response = await post<{ data?: Record }>(`${this.backendUrl}/api/v1/verify`, verificationData); - const { data } = response; - return data ? new BN(data.factorKey, "hex") : undefined; - } - - async verifyRemoteSetup(address: string, code: string): Promise { - const verificationData = { - address, - code, - }; - - const response = await post<{ data?: Record }>(`${this.backendUrl}/api/v1/verify_remote`, verificationData); - const { data } = response; - - return { - tssShareIndex: this.tssIndex, - remoteClientUrl: this.backendUrl, - remoteFactorPub: this.factorPub, - metadataShare: data.metadataShare, - remoteClientToken: data.signature, - }; - } -}