diff --git a/src/interfaces.ts b/src/interfaces.ts index ef5f64d..d27397c 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -495,3 +495,5 @@ export interface Secp256k1PrecomputedClient { client: Client; serverCoeffs: Record; } + +export type PreSigningHookType = (params: { data: Uint8Array; hashed: boolean }) => Promise<{ success: boolean; error?: string; data?: string }>; diff --git a/src/mpcCoreKit.ts b/src/mpcCoreKit.ts index 84a1f42..6b291d2 100644 --- a/src/mpcCoreKit.ts +++ b/src/mpcCoreKit.ts @@ -43,6 +43,7 @@ import { JWTLoginParams, MPCKeyDetails, OAuthLoginParams, + PreSigningHookType, Secp256k1PrecomputedClient, SessionData, SubVerifierDetailsParams, @@ -99,6 +100,8 @@ export class Web3AuthMPCCoreKit implements ICoreKit { private atomicCallStackCounter: number = 0; + private preSigningHook?: PreSigningHookType; + constructor(options: Web3AuthOptions) { if (!options.web3AuthClientId) { throw CoreKitError.clientIdInvalid(); @@ -456,6 +459,10 @@ export class Web3AuthMPCCoreKit implements ICoreKit { } } + setPreSigningHook(preSigningHook: PreSigningHookType) { + this.preSigningHook = preSigningHook; + } + public async handleRedirectResult(): Promise { this.checkReady(); @@ -778,6 +785,12 @@ export class Web3AuthMPCCoreKit implements ICoreKit { } public async sign(data: Buffer, hashed: boolean = false, secp256k1Precompute?: Secp256k1PrecomputedClient): Promise { + if (this.preSigningHook) { + const result = await this.preSigningHook({ data: Uint8Array.from(data), hashed }); + if (!result.success || result.error) { + throw Error(result.error || "preSigningValidator failed"); + } + } this.wasmLib = await this.loadTssWasm(); if (this.keyType === KeyType.secp256k1) { const sig = await this.sign_ECDSA_secp256k1(data, hashed, secp256k1Precompute); diff --git a/tests/login.spec.ts b/tests/login.spec.ts index 56a4851..f0ce429 100644 --- a/tests/login.spec.ts +++ b/tests/login.spec.ts @@ -1,4 +1,4 @@ -import assert from "node:assert"; +import assert, { fail } from "node:assert"; import test from "node:test"; import { EllipticPoint } from "@tkey/common-types"; @@ -166,8 +166,40 @@ variable.forEach((testVariable) => { const signature2 = sigToRSV(await coreKitInstance.sign(msgBuffer)); const pubkey2 = secp256k1.recoverPubKey(msgHash, signature2, signature2.v) as EllipticPoint; assert(pubkey2.eq(publicKeyPoint)); + + // should fail to sign due to preSignValidation + { + coreKitInstance.setPreSigningHook(async ({ data }) => { + return { + success: false, + data: Buffer.from(data).toString("hex") + } + }); + + try { + await coreKitInstance.sign(msgHash, true); + fail("Should fail signing") + } catch (err) { + assert(err); + } + }; + + // should succeed to sign + { + coreKitInstance.setPreSigningHook(async ({ data }) => { + return { + success: true, + data: Buffer.from(data).toString("hex") + } + }); + const signature = sigToRSV(await coreKitInstance.sign(msgHash, true)); + const pubkey = secp256k1.recoverPubKey(msgHash, signature, signature.v) as EllipticPoint; + const publicKeyPoint = bufferToElliptic(coreKitInstance.getPubKey()); + assert(pubkey.eq(publicKeyPoint)); + }; }); + await t.test("#Login and sign with different account/wallet index", async function () { const vid = stringGen(10); const coreKitInstance = newCoreKitInstance();