From ec8a038b2ecaeaf01d0f5a9b6dbaed27f845b853 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Mon, 12 May 2025 14:15:47 -0700 Subject: [PATCH 1/2] Fix accidental duress stash creation in loginWithKey The `loginWithKey` method has a bug where it will call `ensureAccountExists` for the duress account if `duressMode` option is set to true. If we don't have a duress account for the account being logged into, then we shouldn't be creating an duress account from within `loginWithKey`. --- CHANGELOG.md | 1 + src/core/context/context-api.ts | 13 +++++++++---- test/core/login/login.test.ts | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31d6c96a..ebf25f64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- fixed: Bug causing duress account creation from `loginWithKey` while in duress mode. - fixed: Clean the Android build folder, so leftover files don't get published. ## 2.27.2 (2025-05-08) diff --git a/src/core/context/context-api.ts b/src/core/context/context-api.ts index 7d1d39fa..f560d90d 100644 --- a/src/core/context/context-api.ts +++ b/src/core/context/context-api.ts @@ -150,6 +150,13 @@ export function makeContextApi(ai: ApiInput): EdgeContext { throw new Error(`Cannot find requested appId: "${appId}"`) } + // Get the duress stash for existence check: + const duressAppId = appId + '.duress' + const duressStash = searchTree( + stashTree, + stash => stash.appId === duressAppId + ) + let sessionKey: SessionKey try { sessionKey = { @@ -161,9 +168,6 @@ export function makeContextApi(ai: ApiInput): EdgeContext { makeAuthJson(stashTree, sessionKey) } catch (error) { if (error instanceof Error && error.message === 'Invalid checksum') { - const duressStash = searchTree(stashTree, stash => - stash.appId.endsWith('.duress') - ) if (duressStash == null) { throw error } @@ -188,7 +192,8 @@ export function makeContextApi(ai: ApiInput): EdgeContext { return await makeAccount(ai, sessionKey, 'keyLogin', { ...opts, - duressMode: inDuressMode + // We must require that the duress account exists: + duressMode: inDuressMode && duressStash != null }) }, diff --git a/test/core/login/login.test.ts b/test/core/login/login.test.ts index f2ff574b..81cf7280 100644 --- a/test/core/login/login.test.ts +++ b/test/core/login/login.test.ts @@ -547,6 +547,28 @@ describe('duress', function () { expect(topicAccount2.isDuressAccount).equals(true) }) + it('Avoid creating duress account when using loginWithKey', async function () { + const world = await makeFakeEdgeWorld([fakeUser], quiet) + const context = await world.makeEdgeContext(contextOptions) + const otherAccount = await context.createAccount({ + username: 'new-user', + pin: '1111' + }) + const loginKey = await otherAccount.getLoginKey() + await otherAccount.logout() + const account = await context.loginWithPIN(fakeUser.username, fakeUser.pin) + // Create duress account: + await account.changePin({ pin: '0000', forDuressAccount: true }) + await account.logout() + // Activate duress mode: + const duressAccount = await context.loginWithPIN(fakeUser.username, '0000') + await duressAccount.logout() + + // Login to the main account using the loginKey: + const topicAccount = await context.loginWithKey('new-user', loginKey) + expect(topicAccount.isDuressAccount).equals(false) + }) + it('check password', async function () { const world = await makeFakeEdgeWorld([fakeUser], quiet) const context = await world.makeEdgeContext(contextOptions) From bba76257832a9b31908b3e0272a4edb2ede3365b Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Mon, 12 May 2025 14:29:38 -0700 Subject: [PATCH 2/2] Add test cases to avoid accidental duress account creation --- test/core/login/login.test.ts | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/core/login/login.test.ts b/test/core/login/login.test.ts index 81cf7280..8c0d4582 100644 --- a/test/core/login/login.test.ts +++ b/test/core/login/login.test.ts @@ -523,6 +523,50 @@ describe('duress', function () { expect(topicAccount.isDuressAccount).equals(true) }) + it('Avoid creating duress account when using loginWithPassword', async function () { + const world = await makeFakeEdgeWorld([fakeUser], quiet) + const context = await world.makeEdgeContext(contextOptions) + const otherAccount = await context.createAccount({ + username: 'new-user', + pin: '1111' + }) + // Create duress account: + await otherAccount.changePin({ pin: '0000', forDuressAccount: true }) + await otherAccount.logout() + // Activate duress mode: + const duressAccount = await context.loginWithPIN('new-user', '0000') + await duressAccount.logout() + + // Login to the main account using password: + const topicAccount = await context.loginWithPassword( + fakeUser.username, + fakeUser.password + ) + expect(topicAccount.isDuressAccount).equals(false) + }) + + it('Avoid creating duress account when using loginWithPIN', async function () { + const world = await makeFakeEdgeWorld([fakeUser], quiet) + const context = await world.makeEdgeContext(contextOptions) + const otherAccount = await context.createAccount({ + username: 'new-user', + pin: '1111' + }) + // Create duress account: + await otherAccount.changePin({ pin: '0000', forDuressAccount: true }) + await otherAccount.logout() + // Activate duress mode: + const duressAccount = await context.loginWithPIN('new-user', '0000') + await duressAccount.logout() + + // Login to the main account using password: + const topicAccount = await context.loginWithPIN( + fakeUser.username, + fakeUser.pin + ) + expect(topicAccount.isDuressAccount).equals(false) + }) + it('persist duress mode when using loginWithKey', async function () { const world = await makeFakeEdgeWorld([fakeUser], quiet) const context = await world.makeEdgeContext(contextOptions)