diff --git a/src/mpcCoreKit.ts b/src/mpcCoreKit.ts index 1f737b6..a8ffe13 100644 --- a/src/mpcCoreKit.ts +++ b/src/mpcCoreKit.ts @@ -1,4 +1,14 @@ -import { BNString, KeyType, ONE_KEY_DELETE_NONCE, Point, secp256k1, SHARE_DELETED, ShareStore, StringifiedType } from "@tkey/common-types"; +import { + BNString, + KEY_NOT_FOUND, + KeyType, + ONE_KEY_DELETE_NONCE, + Point, + secp256k1, + SHARE_DELETED, + ShareStore, + StringifiedType, +} from "@tkey/common-types"; import { CoreError } from "@tkey/core"; import { ShareSerializationModule } from "@tkey/share-serialization"; import { TorusStorageLayer } from "@tkey/storage-layer-torus"; @@ -323,6 +333,11 @@ export class Web3AuthMPCCoreKit implements ICoreKit { if (this.isNodejsOrRN(this.options.uxMode)) { throw CoreKitError.oauthLoginUnsupported(`Oauth login is NOT supported in ${this.options.uxMode} mode.`); } + + if (this.state.factorKey) { + throw CoreKitError.oauthLoginUnsupported("Instance is alreay login or rehydrated"); + } + const { importTssKey, registerExistingSFAKey } = params; const tkeyServiceProvider = this.torusSp; @@ -518,15 +533,17 @@ export class Web3AuthMPCCoreKit implements ICoreKit { public async inputFactorKey(factorKey: BN): Promise { this.checkReady(); try { - // input tkey device share when required share > 0 ( or not reconstructed ) - // assumption tkey shares will not changed + // always check for valid factor key + const factorKeyPrivate = factorKeyCurve.keyFromPrivate(factorKey.toBuffer()); + const factorPubX = factorKeyPrivate.getPublic().getX().toString("hex").padStart(64, "0"); + const factorEncExist = this.tkey.metadata.factorEncs?.[this.tkey.tssTag]?.[factorPubX]; + if (!factorEncExist) { + throw CoreKitError.providedFactorKeyInvalid("Invalid FactorKey provided. Failed to input factor key."); + } + + // input tkey device share when required share > 0 ( or tkey is not yet reconstructed ) + // assumption tkey shares will never changed if (!this.tKey.secp256k1Key) { - const factorKeyPrivate = factorKeyCurve.keyFromPrivate(factorKey.toBuffer()); - const factorPubX = factorKeyPrivate.getPublic().getX().toString("hex").padStart(64, "0"); - const factorEncExist = this.tkey.metadata.factorEncs?.[this.tkey.tssTag]?.[factorPubX]; - if (!factorEncExist) { - throw CoreKitError.providedFactorKeyInvalid("Invalid FactorKey provided. Failed to input factor key."); - } const factorKeyMetadata = await this.getFactorKeyMetadata(factorKey); await this.tKey.inputShareStoreSafe(factorKeyMetadata, true); } @@ -605,7 +622,6 @@ export class Web3AuthMPCCoreKit implements ICoreKit { const hashedFactorPub = getPubKeyPoint(hashedFactorKey, factorKeyCurve); await this.deleteFactor(hashedFactorPub, hashedFactorKey); - await this.deleteMetadataShareBackup(hashedFactorKey); // only recovery factor = true let backupFactorKey; @@ -1107,6 +1123,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { shareDescription: FactorKeyTypeShareDescription.Other, updateMetadata: false, }); + await this.setDeviceFactor(factorKey); } else { await this.addFactorDescription({ factorKey, @@ -1199,8 +1216,16 @@ export class Web3AuthMPCCoreKit implements ICoreKit { signatures: result.signatures, userInfo: result.userInfo, }); + + // update device factor if not present upon rehydration + if (this.options.disableHashedFactorKey) { + const deviceFactorKey = await this.getDeviceFactor(); + if (!deviceFactorKey && this.state.factorKey && this.state.tssShareIndex === TssShareType.DEVICE) { + await this.setDeviceFactor(this.state.factorKey); + } + } } catch (err) { - log.warn("failed to authorize session", err); + log.warn("failed to authorize session please use new", err); } } @@ -1263,7 +1288,7 @@ export class Web3AuthMPCCoreKit implements ICoreKit { private async getFactorKeyMetadata(factorKey: BN): Promise { this.checkReady(); const factorKeyMetadata = await this.tKey?.readMetadata(factorKey); - if (!factorKeyMetadata || factorKeyMetadata.message === "KEY_NOT_FOUND") { + if (!factorKeyMetadata || factorKeyMetadata.message === KEY_NOT_FOUND || factorKeyMetadata.message === SHARE_DELETED) { throw CoreKitError.noMetadataFound(); } return ShareStore.fromJSON(factorKeyMetadata); @@ -1325,19 +1350,21 @@ export class Web3AuthMPCCoreKit implements ICoreKit { } private async deleteMetadataShareBackup(factorKey: BN): Promise { - await this.tKey.addLocalMetadataTransitions({ input: [{ message: SHARE_DELETED, dateAdded: Date.now() }], privKey: [factorKey] }); - if (!this.tkey?.manualSync) await this.tkey?.syncLocalMetadataTransitions(); + await this.atomicSync(async () => { + await this.tKey.addLocalMetadataTransitions({ input: [{ message: SHARE_DELETED, dateAdded: Date.now() }], privKey: [factorKey] }); + }); } private async backupMetadataShare(factorKey: BN) { const metadataShare = await this.getMetadataShare(); - // Set metadata for factor key backup - await this.tKey?.addLocalMetadataTransitions({ - input: [metadataShare], - privKey: [factorKey], + await this.atomicSync(async () => { + // Set metadata for factor key backup + await this.tKey?.addLocalMetadataTransitions({ + input: [metadataShare], + privKey: [factorKey], + }); }); - if (!this.tkey?.manualSync) await this.tkey?.syncLocalMetadataTransitions(); } private async addFactorDescription(args: { diff --git a/tests/ed25519.spec.ts b/tests/ed25519.spec.ts index 208beb5..07e2277 100644 --- a/tests/ed25519.spec.ts +++ b/tests/ed25519.spec.ts @@ -6,7 +6,7 @@ import { UX_MODE_TYPE } from "@toruslabs/customauth"; import { tssLib } from "@toruslabs/tss-frost-lib"; import BN from "bn.js"; -import { AsyncStorage, COREKIT_STATUS, ed25519, MemoryStorage, WEB3AUTH_NETWORK, WEB3AUTH_NETWORK_TYPE, Web3AuthMPCCoreKit } from "../src"; +import { AsyncStorage, COREKIT_STATUS, ed25519, MemoryStorage, TssShareType, WEB3AUTH_NETWORK, WEB3AUTH_NETWORK_TYPE, Web3AuthMPCCoreKit } from "../src"; import { bufferToElliptic, criticalResetAccount, mockLogin, mockLogin2 } from "./setup"; type TestVariable = { @@ -14,15 +14,19 @@ type TestVariable = { uxMode: UX_MODE_TYPE | "nodejs"; manualSync?: boolean; email: string; + importedEmail: string }; -const defaultTestEmail = "testEmailForLoginEd25519"; +const defaultTestEmail = "testEmailForLoginEd25519-01"; +const importedEmail = "testEmailImportEd25519-01" + const variable: TestVariable[] = [ - { web3AuthNetwork: WEB3AUTH_NETWORK.DEVNET, uxMode: "nodejs", email: defaultTestEmail }, + { web3AuthNetwork: WEB3AUTH_NETWORK.DEVNET, uxMode: "nodejs", email: defaultTestEmail, importedEmail }, // { web3AuthNetwork: WEB3AUTH_NETWORK.MAINNET, uxMode: UX_MODE.REDIRECT, email: defaultTestEmail }, - { web3AuthNetwork: WEB3AUTH_NETWORK.DEVNET, uxMode: "nodejs", manualSync: true, email: defaultTestEmail }, + { web3AuthNetwork: WEB3AUTH_NETWORK.DEVNET, uxMode: "nodejs", manualSync: true, email: defaultTestEmail , importedEmail }, // { web3AuthNetwork: WEB3AUTH_NETWORK.MAINNET, uxMode: UX_MODE.REDIRECT, manualSync: true, email: defaultTestEmail }, + ]; const checkLogin = async (coreKitInstance: Web3AuthMPCCoreKit, accountIndex = 0) => { @@ -39,7 +43,7 @@ const storageInstance = new MemoryStorage(); variable.forEach((testVariable) => { const { web3AuthNetwork, uxMode, manualSync, email } = testVariable; - const newCoreKitInstance = () => + const newCoreKitInstance = ( params: {disableSessionManager : boolean } = {disableSessionManager: true}) => new Web3AuthMPCCoreKit({ web3AuthClientId: "torus-key-test", web3AuthNetwork, @@ -48,11 +52,12 @@ variable.forEach((testVariable) => { tssLib, storage: storageInstance, manualSync, + disableSessionManager: params.disableSessionManager }); - async function resetAccount() { + async function resetAccount( resetEmail: string) { const resetInstance = newCoreKitInstance(); - const { idToken, parsedToken } = await mockLogin(email); + const { idToken, parsedToken } = await mockLogin(resetEmail); await resetInstance.init({ handleRedirectResult: false, rehydrate: false }); await resetInstance.loginWithJWT({ verifier: "torus-test-health", @@ -69,9 +74,11 @@ variable.forEach((testVariable) => { let checkTssShare: BN; test(`#Login Test with JWT + logout: ${testNameSuffix}`, async (t) => { - await resetAccount(); + await resetAccount(email); + await resetAccount(importedEmail); + await t.test("#Login", async function () { - const coreKitInstance = newCoreKitInstance(); + const coreKitInstance = newCoreKitInstance({disableSessionManager: false}); // mocklogin const { idToken, parsedToken } = await mockLogin(email); @@ -98,7 +105,7 @@ variable.forEach((testVariable) => { }); await t.test("#relogin ", async function () { - const coreKitInstance = newCoreKitInstance(); + const coreKitInstance = newCoreKitInstance({ disableSessionManager: false }); // rehydrate await coreKitInstance.init({ handleRedirectResult: false }); await checkLogin(coreKitInstance); @@ -147,5 +154,40 @@ variable.forEach((testVariable) => { const valid = ed25519().verify(msgBuffer, signature, coreKitInstance.getPubKeyEd25519()); assert(valid); }); + + await t.test("#able to export import", async function () { + const coreKitInstance = newCoreKitInstance(); + await coreKitInstance.init({ handleRedirectResult: false, rehydrate: false }); + const localToken = await mockLogin2(email); + await coreKitInstance.loginWithJWT({ + verifier: "torus-test-health", + verifierId: email, + idToken: localToken.idToken, + }); + + await coreKitInstance.enableMFA({}); + if (manualSync) { + await coreKitInstance.commitChanges(); + } + const exportedSeed = await coreKitInstance._UNSAFE_exportTssEd25519Seed(); + + // import tss key into mpc corekit + const coreKitInstance2 = newCoreKitInstance(); + await coreKitInstance2.init({ handleRedirectResult: false, rehydrate: false }); + const localToken2 = await mockLogin2(importedEmail); + await coreKitInstance2.loginWithJWT({ + verifier: "torus-test-health", + verifierId: importedEmail, + idToken: localToken2.idToken, + importTssKey: exportedSeed.toString("hex"), + }); + if (manualSync) { + await coreKitInstance2.commitChanges(); + } + + const exportedSeed2 = await coreKitInstance2._UNSAFE_exportTssEd25519Seed(); + + assert(exportedSeed.toString("hex") === (exportedSeed2.toString("hex"))); + }); }); }); diff --git a/tests/factors.spec.ts b/tests/factors.spec.ts index 01b3833..dff86ab 100644 --- a/tests/factors.spec.ts +++ b/tests/factors.spec.ts @@ -1,14 +1,15 @@ import assert from "node:assert"; import test from "node:test"; -import { EllipticPoint, Point } from "@tkey/common-types"; +import { EllipticPoint, KeyType, Point, secp256k1 } from "@tkey/common-types"; import { factorKeyCurve } from "@tkey/tss"; 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, TssLibType, TssShareType, WEB3AUTH_NETWORK, Web3AuthMPCCoreKit } from "../src"; +import { AsyncStorage, COREKIT_STATUS, ed25519, IAsyncStorage, IStorage, MemoryStorage, sigToRSV, TssLibType, TssShareType, WEB3AUTH_NETWORK, Web3AuthMPCCoreKit } from "../src"; import { AsyncMemoryStorage, bufferToElliptic, criticalResetAccount, mockLogin } from "./setup"; +import { keccak256 } from "@toruslabs/metadata-helpers"; type FactorTestVariable = { manualSync?: boolean; @@ -28,6 +29,26 @@ function getPubKeys(kit: Web3AuthMPCCoreKit, indices: number[]): EllipticPoint[] return pubKeys; } +async function signSecp256k1Data( params : { coreKitInstance: Web3AuthMPCCoreKit, msg: string, }) { + const {coreKitInstance, msg } = params + const msgBuffer1 = Buffer.from(msg); + const msgHash = keccak256(msgBuffer1); + + 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)); +} + +async function signEd25519Data(params: { coreKitInstance: Web3AuthMPCCoreKit, msg: string }) { + const { coreKitInstance, msg } = params; + const msgBuffer = Buffer.from(msg) + const signature = ed25519().makeSignature((await coreKitInstance.sign(msgBuffer)).toString("hex")); + const valid = ed25519().verify(msgBuffer, signature, coreKitInstance.getPubKeyEd25519()); + assert(valid); +} + export const FactorManipulationTest = async (testVariable: FactorTestVariable) => { const { email, tssLib } = testVariable; const newInstance = async () => { @@ -39,6 +60,7 @@ export const FactorManipulationTest = async (testVariable: FactorTestVariable) = tssLib: tssLib || tssLibDKLS, storage: testVariable.storage, manualSync: testVariable.manualSync, + disableSessionManager: true }); const { idToken, parsedToken } = await mockLogin(email); @@ -55,6 +77,7 @@ export const FactorManipulationTest = async (testVariable: FactorTestVariable) = const resetInstance = await newInstance(); await criticalResetAccount(resetInstance); await resetInstance.logout(); + await new AsyncStorage(resetInstance._storageKey, testVariable.storage).resetStore(); } await test(`#Factor manipulation - manualSync ${testVariable.manualSync} `, async function (t) { @@ -163,6 +186,7 @@ export const FactorManipulationTest = async (testVariable: FactorTestVariable) = // login with mfa factor await instance2.inputFactorKey(new BN(recoverFactor, "hex")); assert.strictEqual(instance2.status, COREKIT_STATUS.LOGGED_IN); + await instance2.logout(); // new instance @@ -180,16 +204,24 @@ export const FactorManipulationTest = async (testVariable: FactorTestVariable) = await instance3.inputFactorKey(new BN(browserFactor, "hex")); assert.strictEqual(instance3.status, COREKIT_STATUS.LOGGED_IN); + + if ( tssLib && tssLib.keyType === KeyType.ed25519) { + await signEd25519Data({ coreKitInstance: instance3, msg: "hello world" }); + } else { + await signSecp256k1Data({ coreKitInstance: instance3, msg: "hello world" }); + } + }); + }); }; const variable: FactorTestVariable[] = [ - { manualSync: true, storage: new MemoryStorage(), email: "testmail1012" }, - { manualSync: false, storage: new MemoryStorage(), email: "testmail1013" }, + { manualSync: true, storage: new MemoryStorage(), email: "testmail1012-1" }, + { manualSync: false, storage: new MemoryStorage(), email: "testmail1013-1" }, - { manualSync: true, storage: new AsyncMemoryStorage(), email: "testmail1014" }, - { manualSync: false, storage: new AsyncMemoryStorage(), email: "testmail1015" }, + { manualSync: true, storage: new AsyncMemoryStorage(), email: "testmail1014-1" }, + { manualSync: false, storage: new AsyncMemoryStorage(), email: "testmail1015-1" }, { manualSync: true, storage: new MemoryStorage(), email: "testmail1012ed25519", tssLib: tssLibFROST }, ]; diff --git a/tests/importRecovery.spec.ts b/tests/importRecovery.spec.ts index 8d3bbc8..651b345 100644 --- a/tests/importRecovery.spec.ts +++ b/tests/importRecovery.spec.ts @@ -4,8 +4,10 @@ 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, TssLibType, TssShareType, WEB3AUTH_NETWORK } from "../src"; -import { bufferToElliptic, criticalResetAccount, newCoreKitLogInInstance } from "./setup"; +import { MemoryStorage, sigToRSV, TssLibType, TssShareType, WEB3AUTH_NETWORK, Web3AuthMPCCoreKit } from "../src"; +import { bufferToElliptic, criticalResetAccount, mockLogin } from "./setup"; +import { EllipticPoint, KeyType, secp256k1 } from "@tkey/common-types"; +import { keccak256 } from "@toruslabs/metadata-helpers"; type ImportKeyTestVariable = { manualSync?: boolean; @@ -13,25 +15,52 @@ type ImportKeyTestVariable = { importKeyEmail: string; tssLib: TssLibType; }; +async function signSecp256k1Data( params : { coreKitInstance: Web3AuthMPCCoreKit, msg: string, }) { + const {coreKitInstance, msg } = params + const msgBuffer1 = Buffer.from(msg); + const msgHash = keccak256(msgBuffer1); + 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)); +} const storageInstance = new MemoryStorage(); export const ImportTest = async (testVariable: ImportKeyTestVariable) => { async function newCoreKitInstance(email: string, importTssKey?: string) { - return newCoreKitLogInInstance({ - network: WEB3AUTH_NETWORK.DEVNET, - manualSync: testVariable.manualSync, - email: email, - storageInstance, - tssLib: testVariable.tssLib, - importTssKey, - }); + const instance = new Web3AuthMPCCoreKit({ + web3AuthClientId: "torus-key-test", + web3AuthNetwork: WEB3AUTH_NETWORK.DEVNET, + baseUrl: "http://localhost:3000", + uxMode: "nodejs", + tssLib: testVariable.tssLib, + storage: storageInstance, + manualSync: testVariable.manualSync, + disableSessionManager: true + }); + + const { idToken, parsedToken } = await mockLogin(email); + await instance.init({ handleRedirectResult: false, rehydrate: false }); + await instance.loginWithJWT({ + verifier: "torus-test-health", + verifierId: parsedToken.email, + idToken, + importTssKey + }); + return instance; + } async function resetAccount(email: string) { const kit = await newCoreKitInstance(email); + console.log('tss pub key', kit.state.tssPubKey) await criticalResetAccount(kit); + if (testVariable.manualSync) { + await kit.commitChanges(); + } await kit.logout(); - await new AsyncStorage(kit._storageKey, storageInstance).resetStore(); + // await new AsyncStorage(kit._storageKey, storageInstance).resetStore(); } test(`import recover tss key : ${testVariable.manualSync}`, async function (t) { @@ -50,6 +79,9 @@ export const ImportTest = async (testVariable: ImportKeyTestVariable) => { shareType: TssShareType.DEVICE, }); + if (testVariable.tssLib.keyType === KeyType.secp256k1) { + await signSecp256k1Data({ coreKitInstance, msg: "hello world" }); + } const factorKeyRecovery = await coreKitInstance.createFactor({ shareType: TssShareType.RECOVERY, }); @@ -62,8 +94,21 @@ export const ImportTest = async (testVariable: ImportKeyTestVariable) => { const exportedTssKey1 = await coreKitInstance._UNSAFE_exportTssKey(); await coreKitInstance.logout(); + const instance = new Web3AuthMPCCoreKit({ + web3AuthClientId: "torus-key-test", + web3AuthNetwork: WEB3AUTH_NETWORK.DEVNET, + baseUrl: "http://localhost:3000", + uxMode: "nodejs", + tssLib: testVariable.tssLib, + storage: storageInstance, + manualSync: testVariable.manualSync, + disableSessionManager: true + }); + + await instance.init({rehydrate: false, handleRedirectResult: false}) + // Recover key from any two factors. - const recoveredTssKey = await coreKitInstance._UNSAFE_recoverTssKey([factorKeyDevice, factorKeyRecovery]); + const recoveredTssKey = await instance._UNSAFE_recoverTssKey([factorKeyDevice, factorKeyRecovery]); assert.strictEqual(recoveredTssKey, exportedTssKey1); // Initialize new instance and import existing key. @@ -105,9 +150,9 @@ export const ImportTest = async (testVariable: ImportKeyTestVariable) => { }; const variable: ImportKeyTestVariable[] = [ - { manualSync: false, email: "emailexport", importKeyEmail: "emailimport", tssLib: tssLibDKLS }, - { manualSync: true, email: "emailexport", importKeyEmail: "emailimport", tssLib: tssLibDKLS }, - { manualSync: false, email: "emailexport_ed25519", importKeyEmail: "emailimport_ed25519", tssLib: tssLibFROST }, + { manualSync: false, email: "emailexport-01", importKeyEmail: "emailimport-001", tssLib: tssLibDKLS }, + { manualSync: true, email: "emailexport-01", importKeyEmail: "emailimport-001", tssLib: tssLibDKLS }, + // { manualSync: false, email: "emailexport_ed25519", importKeyEmail: "emailimport_ed25519", tssLib: tssLibFROST }, ]; variable.forEach(async (testVariable) => { diff --git a/tests/login-disableHashedFactorKey.spec.ts b/tests/login-disableHashedFactorKey.spec.ts new file mode 100644 index 0000000..e46d6a8 --- /dev/null +++ b/tests/login-disableHashedFactorKey.spec.ts @@ -0,0 +1,295 @@ +import assert, { fail } from "node:assert"; +import test from "node:test"; + +import { EllipticPoint } from "@tkey/common-types"; +import { UX_MODE_TYPE } from "@toruslabs/customauth"; +import { keccak256 } from "@toruslabs/metadata-helpers"; +import { tssLib } from "@toruslabs/tss-dkls-lib"; +import BN from "bn.js"; +import { ec as EC } from "elliptic"; + +import { AsyncStorage, COREKIT_STATUS, MemoryStorage, sigToRSV, WEB3AUTH_NETWORK, WEB3AUTH_NETWORK_TYPE, Web3AuthMPCCoreKit } from "../src"; +import { bufferToElliptic, criticalResetAccount, mockLogin, mockLogin2, stringGen } from "./setup"; + +type TestVariable = { + web3AuthNetwork: WEB3AUTH_NETWORK_TYPE; + uxMode: UX_MODE_TYPE | "nodejs"; + manualSync?: boolean; + + email: string; +}; + +const defaultTestEmail = "testEmailForLogin-disableHashedFactorKey"; +const variable: TestVariable[] = [ + { web3AuthNetwork: WEB3AUTH_NETWORK.DEVNET, uxMode: "nodejs", email: defaultTestEmail }, + // { web3AuthNetwork: WEB3AUTH_NETWORK.MAINNET, uxMode: UX_MODE.REDIRECT, email: defaultTestEmail }, + + { web3AuthNetwork: WEB3AUTH_NETWORK.DEVNET, uxMode: "nodejs", manualSync: true, email: defaultTestEmail }, + // { web3AuthNetwork: WEB3AUTH_NETWORK.MAINNET, uxMode: UX_MODE.REDIRECT, manualSync: true, email: defaultTestEmail }, +]; + +const checkLogin = async (coreKitInstance: Web3AuthMPCCoreKit, accountIndex = 0) => { + const keyDetails = coreKitInstance.getKeyDetails(); + assert.strictEqual(coreKitInstance.status, COREKIT_STATUS.LOGGED_IN); + assert.strictEqual(keyDetails.requiredFactors, 0); + const factorkey = coreKitInstance.getCurrentFactorKey(); + await coreKitInstance.tKey.getTSSShare(new BN(factorkey.factorKey, "hex"), { + accountIndex, + }); +}; + +const storageInstance = new MemoryStorage(); + +variable.forEach((testVariable) => { + const { web3AuthNetwork, uxMode, manualSync, email } = testVariable; + const newCoreKitInstance = () => + new Web3AuthMPCCoreKit({ + web3AuthClientId: "torus-key-test", + web3AuthNetwork, + baseUrl: "http://localhost:3000", + uxMode, + tssLib, + storage: storageInstance, + manualSync, + disableHashedFactorKey: true, + disableSessionManager: true + }); + + const testNameSuffix = JSON.stringify(testVariable); + + let checkPubKey: EllipticPoint; + let checkTssShare: BN; + + test(`#Login Test with JWT + logout : ${testNameSuffix}`, async (t) => { + async function beforeTest() { + const resetInstance = new Web3AuthMPCCoreKit({ + web3AuthClientId: "torus-key-test", + web3AuthNetwork, + baseUrl: "http://localhost:3000", + uxMode, + tssLib, + storage: storageInstance, + manualSync, + }); + const { idToken, parsedToken } = await mockLogin(email); + await resetInstance.init({ handleRedirectResult: false, rehydrate: false }); + await resetInstance.loginWithJWT({ + verifier: "torus-test-health", + verifierId: parsedToken.email, + idToken, + }); + await criticalResetAccount(resetInstance); + await new AsyncStorage(resetInstance._storageKey, storageInstance).resetStore(); + } + + await beforeTest(); + await t.test("#Login", async function () { + const coreKitInstance = newCoreKitInstance(); + + // mocklogin + const { idToken, parsedToken } = await mockLogin(email); + await coreKitInstance.init({ handleRedirectResult: false }); + await coreKitInstance.loginWithJWT({ + verifier: "torus-test-health", + verifierId: parsedToken.email, + idToken, + }); + // get key details + await checkLogin(coreKitInstance); + + checkPubKey = bufferToElliptic(coreKitInstance.getPubKey()); + const factorkey = coreKitInstance.getCurrentFactorKey(); + const { tssShare } = await coreKitInstance.tKey.getTSSShare(new BN(factorkey.factorKey, "hex"), { + threshold: 0, + }); + checkTssShare = tssShare; + + if (manualSync) { + await coreKitInstance.commitChanges(); + } + // check whether the public key and tss share is same as old sdks + }); + + await t.test("#relogin ", async function () { + const coreKitInstance = newCoreKitInstance(); + + + // logout + await coreKitInstance.logout(); + + // rehydrate should fail + await coreKitInstance.init({ + rehydrate: false, + handleRedirectResult: false, + }); + assert.strictEqual(coreKitInstance.status, COREKIT_STATUS.INITIALIZED); + assert.throws(() => coreKitInstance.getCurrentFactorKey()); + + // relogin + const { idToken, parsedToken } = await mockLogin(email); + await coreKitInstance.loginWithJWT({ + verifier: "torus-test-health", + verifierId: parsedToken.email, + idToken, + }); + + const deviceFactorKey = await coreKitInstance.getDeviceFactor(); + await coreKitInstance.inputFactorKey(new BN(deviceFactorKey, "hex")); + // get key details + await checkLogin(coreKitInstance); + const newPubKey = bufferToElliptic(coreKitInstance.getPubKey()); + const factorkey = coreKitInstance.getCurrentFactorKey(); + const { tssShare: newTssShare } = await coreKitInstance.tKey.getTSSShare(new BN(factorkey.factorKey, "hex")); + assert(checkPubKey.eq(newPubKey)); + assert(checkTssShare.eq(newTssShare)); + }); + + await t.test("#able to sign", async function () { + const coreKitInstance = newCoreKitInstance(); + await coreKitInstance.init({ handleRedirectResult: false, rehydrate: false }); + const localToken = await mockLogin2(email); + await coreKitInstance.loginWithJWT({ + verifier: "torus-test-health", + verifierId: email, + idToken: localToken.idToken, + }); + + const deviceFactorKey = await coreKitInstance.getDeviceFactor(); + await coreKitInstance.inputFactorKey(new BN(deviceFactorKey, "hex")); + + const msg = "hello world"; + const msgBuffer = Buffer.from(msg); + const msgHash = keccak256(msgBuffer); + const secp256k1 = new EC("secp256k1"); + + // Sign hash. + 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)); + + // Sign full message. + 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(); + // mock login with random + const { idToken: idToken2, parsedToken: parsedToken2 } = await mockLogin(vid); + await coreKitInstance.init({ handleRedirectResult: false, rehydrate: false }); + await coreKitInstance.loginWithJWT({ + verifier: "torus-test-health", + verifierId: parsedToken2.email, + idToken: idToken2, + }); + + const deviceFactorKey = await coreKitInstance.getDeviceFactor(); + await coreKitInstance.inputFactorKey(new BN(deviceFactorKey, "hex")); + + const secp256k1 = new EC("secp256k1"); + await coreKitInstance.setTssWalletIndex(0); + + const msg = "hello world 1"; + const msgBuffer = Buffer.from(msg); + const msgHash = keccak256(msgBuffer); + const signature1 = sigToRSV(await coreKitInstance.sign(msgHash, true)); + + const pubkeyIndex0 = secp256k1.recoverPubKey(msgHash, signature1, signature1.v); + const publicKeyPoint0 = bufferToElliptic(coreKitInstance.getPubKey()); + assert(pubkeyIndex0.eq(publicKeyPoint0)); + + await coreKitInstance.setTssWalletIndex(1); + + const msg1 = "hello world 2"; + const msgBuffer1 = Buffer.from(msg1); + const msgHash1 = keccak256(msgBuffer1); + + const signature2 = sigToRSV(await coreKitInstance.sign(msgHash1, true)); + + const pubkeyIndex1 = secp256k1.recoverPubKey(msgHash1, signature2, signature2.v) as EllipticPoint; + const publicKeyPoint1 = bufferToElliptic(coreKitInstance.getPubKey()); + assert(pubkeyIndex1.eq(publicKeyPoint1)); + + await checkLogin(coreKitInstance, 1); + + await coreKitInstance.setTssWalletIndex(2); + + const msg2 = "hello world 3"; + const msgBuffer2 = Buffer.from(msg2); + const msgHash2 = keccak256(msgBuffer2); + const signature3 = sigToRSV(await coreKitInstance.sign(msgHash2, true)); + + const pubkeyIndex2 = secp256k1.recoverPubKey(msgHash2, signature3, signature3.v) as EllipticPoint; + const publicKeyPoint2 = bufferToElliptic(coreKitInstance.getPubKey()); + assert(pubkeyIndex2.eq(publicKeyPoint2)); + + await checkLogin(coreKitInstance, 2); + + assert(!publicKeyPoint0.eq(publicKeyPoint1)); + assert(!publicKeyPoint0.eq(publicKeyPoint2)); + assert(!publicKeyPoint1.eq(publicKeyPoint2)); + + if (manualSync) { + await coreKitInstance.commitChanges(); + } + const coreKitInstance3 = newCoreKitInstance(); + // mock login with random + const { idToken: idToken3, parsedToken: parsedToken3 } = await mockLogin(vid); + await coreKitInstance3.init({ handleRedirectResult: false, rehydrate: false }); + await coreKitInstance3.loginWithJWT({ + verifier: "torus-test-health", + verifierId: parsedToken3.email, + idToken: idToken3, + }); + + const deviceFactorKey3 = await coreKitInstance3.getDeviceFactor(); + await coreKitInstance3.inputFactorKey(new BN(deviceFactorKey3, "hex")); + + coreKitInstance.setTssWalletIndex(0); + const pubkey3index0 = bufferToElliptic(coreKitInstance3.getPubKey()); + coreKitInstance3.setTssWalletIndex(1); + const pubkey3index1 = bufferToElliptic(coreKitInstance3.getPubKey()); + coreKitInstance3.setTssWalletIndex(2); + const pubkey3index2 = bufferToElliptic(coreKitInstance3.getPubKey()); + + assert(pubkeyIndex0.eq(pubkey3index0)); + assert(pubkeyIndex1.eq(pubkey3index1)); + assert(pubkeyIndex2.eq(pubkey3index2)); + }); + }); +}); diff --git a/tests/securityQuestion.spec.ts b/tests/securityQuestion.spec.ts index 3f1d788..61ee961 100644 --- a/tests/securityQuestion.spec.ts +++ b/tests/securityQuestion.spec.ts @@ -123,7 +123,7 @@ const variable: TestVariable[] = [ // { web3AuthNetwork: WEB3AUTH_NETWORK.MAINNET, uxMode: UX_MODE.REDIRECT, manualSync: true }, ]; -const email = "testmail99"; +const email = "testmail99-1"; variable.forEach(async (testVariable) => { const newCoreKitLogInInstance = async () => { diff --git a/tests/setup.ts b/tests/setup.ts index ab677bf..7462acc 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -30,14 +30,10 @@ export const criticalResetAccount = async (coreKitInstance: Web3AuthMPCCoreKit): throw new Error("coreKitInstance is not set"); } - if (coreKitInstance.tKey.secp256k1Key) { - await coreKitInstance.tKey.CRITICAL_deleteTkey(); - } else { - await coreKitInstance.tKey.storageLayer.setMetadata({ - privKey: new BN(coreKitInstance.state.postBoxKey!, "hex"), - input: { message: "KEY_NOT_FOUND" }, - }); - } + await coreKitInstance.tKey.storageLayer.setMetadata({ + privKey: new BN(coreKitInstance.state.postBoxKey!, "hex"), + input: { message: "KEY_NOT_FOUND" }, + }); }; const privateKey = "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCD7oLrcKae+jVZPGx52Cb/lKhdKxpXjl9eGNa1MlY57A=="; @@ -114,7 +110,7 @@ export const newCoreKitLogInInstance = async ({ }); const { idToken, parsedToken } = login ? await login(email) : await mockLogin(email); - await instance.init(); + await instance.init({ handleRedirectResult: false, rehydrate: false }); await instance.loginWithJWT({ verifier: "torus-test-health", verifierId: parsedToken.email,