From c488643ae0f6ceea6193a806a39929a7b87fb0f3 Mon Sep 17 00:00:00 2001 From: William Swanson Date: Sat, 24 Aug 2024 03:32:06 -0700 Subject: [PATCH] Simplify `makeAccount` arguments We just require the decryption keys now. --- src/core/account/account-api.ts | 18 ++-- src/core/account/account-init.ts | 129 +++++++++++----------- src/core/account/account-pixie.ts | 4 +- src/core/account/account-reducer.ts | 55 +++++----- src/core/account/lobby-api.ts | 44 ++++---- src/core/actions.ts | 6 +- src/core/context/context-api.ts | 41 ++++--- src/core/login/create.ts | 41 +++---- src/core/login/edge.ts | 19 ++-- src/core/login/keys.ts | 8 +- src/core/login/login-secret.ts | 3 - src/core/login/login-selectors.ts | 13 ++- src/core/login/login-types.ts | 17 +-- src/core/login/login-username.ts | 7 -- src/core/login/login.ts | 160 +++++++++++++++++----------- src/core/login/otp.ts | 25 ++--- src/core/login/password.ts | 37 +++---- src/core/login/pin2.ts | 16 +-- src/core/login/recovery2.ts | 16 ++- src/core/login/splitting.ts | 6 +- 20 files changed, 339 insertions(+), 326 deletions(-) diff --git a/src/core/account/account-api.ts b/src/core/account/account-api.ts index 151d783ce..27a18a89d 100644 --- a/src/core/account/account-api.ts +++ b/src/core/account/account-api.ts @@ -383,13 +383,13 @@ export function makeAccountApi(ai: ApiInput, accountId: string): EdgeAccount { }, async createWallet(walletType: string, keys?: object): Promise { + const { sessionKey } = accountState() + // For crash errors: ai.props.log.breadcrumb('EdgeAccount.createWallet', {}) - const { login, loginTree } = accountState() - const walletInfo = await makeCurrencyWalletKeys(ai, walletType, { keys }) - await applyKit(ai, loginTree, makeKeysKit(ai, login, [walletInfo])) + await applyKit(ai, sessionKey, makeKeysKit(ai, sessionKey, [walletInfo])) return walletInfo.id }, @@ -499,13 +499,13 @@ export function makeAccountApi(ai: ApiInput, accountId: string): EdgeAccount { walletType: string, opts: EdgeCreateCurrencyWalletOptions = {} ): Promise { + const { sessionKey } = accountState() + // For crash errors: ai.props.log.breadcrumb('EdgeAccount.createCurrencyWallet', {}) - const { login, loginTree } = accountState() - const walletInfo = await makeCurrencyWalletKeys(ai, walletType, opts) - await applyKit(ai, loginTree, makeKeysKit(ai, login, [walletInfo])) + await applyKit(ai, sessionKey, makeKeysKit(ai, sessionKey, [walletInfo])) return await finishWalletCreation(ai, accountId, walletInfo.id, opts) }, @@ -524,11 +524,11 @@ export function makeAccountApi(ai: ApiInput, accountId: string): EdgeAccount { async createCurrencyWallets( createWallets: EdgeCreateCurrencyWallet[] ): Promise>> { + const { sessionKey } = accountState() + // For crash errors: ai.props.log.breadcrumb('EdgeAccount.makeMemoryWallet', {}) - const { login, loginTree } = accountState() - // Create the keys: const walletInfos = await Promise.all( createWallets.map( @@ -537,7 +537,7 @@ export function makeAccountApi(ai: ApiInput, accountId: string): EdgeAccount { ) // Store the keys on the server: - await applyKit(ai, loginTree, makeKeysKit(ai, login, walletInfos)) + await applyKit(ai, sessionKey, makeKeysKit(ai, sessionKey, walletInfos)) // Set up options: return await Promise.all( diff --git a/src/core/account/account-init.ts b/src/core/account/account-init.ts index 3a23057f9..739a3ad2e 100644 --- a/src/core/account/account-init.ts +++ b/src/core/account/account-init.ts @@ -1,61 +1,55 @@ import { base64 } from 'rfc4648' import { EdgeAccount, EdgeAccountOptions } from '../../types/types' -import { LoginCreateOpts, makeCreateKit } from '../login/create' +import { decryptText } from '../../util/crypto/crypto' +import { makeCreateKit } from '../login/create' import { findFirstKey, makeAccountType, makeKeyInfo, makeKeysKit } from '../login/keys' -import { applyKit, searchTree } from '../login/login' +import { + applyKit, + decryptChildKey, + decryptKeyInfos, + searchTree +} from '../login/login' +import { getStashById } from '../login/login-selectors' import { LoginStash } from '../login/login-stash' -import { LoginKit, LoginTree, LoginType } from '../login/login-types' +import { LoginKit, LoginType, SessionKey } from '../login/login-types' import { createStorageKeys, wasEdgeStorageKeys } from '../login/storage-keys' import { ApiInput, RootProps } from '../root-pixie' -function checkLogin(login: LoginTree): void { - if (login == null || login.loginKey == null) { - throw new Error('Incomplete login') - } -} - -export function findAppLogin(loginTree: LoginTree, appId: string): LoginTree { - const out = searchTree(loginTree, login => login.appId === appId) - if (out == null) { - throw new Error(`Internal error: cannot find login for ${appId}`) - } - return out -} - /** * Creates a child login under the provided login, with the given appId. */ async function createChildLogin( ai: ApiInput, - loginTree: LoginTree, - login: LoginTree, + stashTree: LoginStash, + sessionKey: SessionKey, appId: string -): Promise { - checkLogin(login) - - const opts: LoginCreateOpts = { - pin: loginTree.pin, - username: loginTree.username +): Promise { + let pin: string | undefined + if (stashTree.pin2TextBox != null) { + pin = decryptText(stashTree.pin2TextBox, sessionKey.loginKey) } - opts.keyInfo = makeKeyInfo( - makeAccountType(appId), - wasEdgeStorageKeys(createStorageKeys(ai)) - ) - const kit = await makeCreateKit(ai, login, appId, opts) + + const { kit } = await makeCreateKit(ai, sessionKey, appId, { + keyInfo: makeKeyInfo( + makeAccountType(appId), + wasEdgeStorageKeys(createStorageKeys(ai)) + ), + pin, + username: stashTree.username + }) const parentKit: LoginKit = { - login: { children: [kit.login as LoginTree] }, - loginId: login.loginId, + loginId: sessionKey.loginId, server: kit.server, serverPath: kit.serverPath, stash: { children: [kit.stash as LoginStash] } } - return await applyKit(ai, loginTree, parentKit) + return await applyKit(ai, sessionKey, parentKit) } /** @@ -65,37 +59,45 @@ async function createChildLogin( */ export async function ensureAccountExists( ai: ApiInput, - loginTree: LoginTree, + stashTree: LoginStash, + sessionKey: SessionKey, appId: string -): Promise { - // For crash errors: - ai.props.log.breadcrumb('ensureAccountExists', {}) - - const accountType = makeAccountType(appId) +): Promise { + const { log } = ai.props // If there is no app login, make that: - const login = searchTree(loginTree, login => login.appId === appId) - if (login == null) { + const appStash = searchTree(stashTree, stash => stash.appId === appId) + if (appStash == null) { // For crash errors: ai.props.log.breadcrumb('createChildLogin', {}) - return await createChildLogin(ai, loginTree, loginTree, appId) + + return await createChildLogin(ai, stashTree, sessionKey, appId) } - // Otherwise, make the repo: - if (findFirstKey(login.keyInfos, accountType) == null) { + // Decrypt the wallet keys: + // TODO: Once we cache public keys, use those instead: + const appKey = decryptChildKey(stashTree, sessionKey, appStash.loginId) + const keyInfos = decryptKeyInfos(appStash, appKey.loginKey) + log.warn( + `Login: decrypted keys for user ${base64.stringify(stashTree.loginId)}` + ) + + // If the account has no repo, make one: + const accountType = makeAccountType(appId) + if (findFirstKey(keyInfos, accountType) == null) { // For crash errors: ai.props.log.breadcrumb('createAccountRepo', {}) - checkLogin(login) + const keyInfo = makeKeyInfo( accountType, wasEdgeStorageKeys(createStorageKeys(ai)) ) - const keysKit = makeKeysKit(ai, login, [keyInfo]) - return await applyKit(ai, loginTree, keysKit) + const keysKit = makeKeysKit(ai, appKey, [keyInfo]) + return await applyKit(ai, sessionKey, keysKit) } // Everything is fine, so do nothing: - return loginTree + return stashTree } /** @@ -103,36 +105,30 @@ export async function ensureAccountExists( */ export async function makeAccount( ai: ApiInput, - appId: string, - loginTree: LoginTree, + sessionKey: SessionKey, loginType: LoginType, opts: EdgeAccountOptions ): Promise { - // For crash errors: - ai.props.log.breadcrumb('makeAccount', {}) - const { pauseWallets = false } = opts + const { appId } = ai.props.state.login const { log } = ai.props - log.warn( - `Login: decrypted keys for user ${base64.stringify(loginTree.loginId)}` - ) - loginTree = await ensureAccountExists(ai, loginTree, appId) + // For crash errors: + ai.props.log.breadcrumb('makeAccount', {}) + + // Create the loginTree: + const { stashTree } = getStashById(ai, sessionKey.loginId) + await ensureAccountExists(ai, stashTree, sessionKey, appId) log.warn('Login: account exists for appId') // Add the login to redux: - const hasRootKey = loginTree.loginKey != null ai.props.dispatch({ type: 'LOGIN', payload: { - appId, - hasRootKey, - loginKey: hasRootKey - ? loginTree.loginKey - : findAppLogin(loginTree, appId).loginKey, loginType, pauseWallets, - rootLoginId: loginTree.loginId + rootLoginId: stashTree.loginId, + sessionKey } }) @@ -142,10 +138,7 @@ export async function makeAccount( /** * Waits for the account API to appear and returns it. */ -export function waitForAccount( - ai: ApiInput, - accountId: string -): Promise { +function waitForAccount(ai: ApiInput, accountId: string): Promise { const out: Promise = ai.waitFor( (props: RootProps): EdgeAccount | undefined => { const accountState = props.state.accounts[accountId] diff --git a/src/core/account/account-pixie.ts b/src/core/account/account-pixie.ts index 5448f997e..d5dcf0bb1 100644 --- a/src/core/account/account-pixie.ts +++ b/src/core/account/account-pixie.ts @@ -142,8 +142,8 @@ const accountPixie: TamePixie = combinePixies({ const ai = toApiInput(input) const { accountId } = input.props if (input.props.state.accounts[accountId] == null) return - const { login, loginTree } = input.props.state.accounts[accountId] - await syncLogin(ai, loginTree, login) + const { sessionKey } = input.props.state.accounts[accountId] + await syncLogin(ai, sessionKey) } // We don't report sync failures, since that could be annoying: diff --git a/src/core/account/account-reducer.ts b/src/core/account/account-reducer.ts index 6cc70851f..3343da007 100644 --- a/src/core/account/account-reducer.ts +++ b/src/core/account/account-reducer.ts @@ -11,12 +11,16 @@ import { compare } from '../../util/compare' import { verifyData } from '../../util/crypto/verify' import { RootAction } from '../actions' import { findFirstKey, getAllWalletInfos, makeAccountType } from '../login/keys' -import { makeLoginTree } from '../login/login' +import { makeLoginTree, searchTree } from '../login/login' import { LoginStash } from '../login/login-stash' -import { LoginTree, LoginType, WalletInfoFullMap } from '../login/login-types' +import { + LoginTree, + LoginType, + SessionKey, + WalletInfoFullMap +} from '../login/login-types' import { maybeFindCurrencyPluginId } from '../plugins/plugins-selectors' import { RootState } from '../root-reducer' -import { findAppLogin } from './account-init' import { SwapSettings } from './account-types' export interface AccountState { @@ -37,14 +41,12 @@ export interface AccountState { readonly pauseWallets: boolean // Login stuff: - readonly appId: string // Copy of the context appId - readonly hasRootKey: boolean // True if the loginKey is for the root readonly loadFailure: Error | null // Failed to create API object. readonly login: LoginTree - readonly loginKey: Uint8Array readonly loginTree: LoginTree readonly loginType: LoginType readonly rootLoginId: Uint8Array + readonly sessionKey: SessionKey readonly stashTree: LoginStash // Plugin stuff: @@ -63,10 +65,14 @@ export interface AccountNext { } export const initialCustomTokens: EdgePluginMap = {} +const blankSessionKey = { + loginId: new Uint8Array(), + loginKey: new Uint8Array() +} const accountInner = buildReducer({ accountWalletInfo: memoizeReducer( - (next: AccountNext) => next.self.appId, + (next: AccountNext) => next.root.login.appId, (next: AccountNext) => next.self.login, (appId: string, login: LoginTree): EdgeWalletInfo => { const type = makeAccountType(appId) @@ -79,7 +85,7 @@ const accountInner = buildReducer({ ), accountWalletInfos: memoizeReducer( - (next: AccountNext) => next.self.appId, + (next: AccountNext) => next.root.login.appId, (next: AccountNext) => next.self.login, (appId: string, login: LoginTree): EdgeWalletInfo[] => { // Wallets created in Edge that then log into Airbitz or BitcoinPay @@ -209,14 +215,6 @@ const accountInner = buildReducer({ return action.type === 'LOGIN' ? action.payload.pauseWallets : state }, - appId(state = '', action): string { - return action.type === 'LOGIN' ? action.payload.appId : state - }, - - hasRootKey(state = true, action): boolean { - return action.type === 'LOGIN' ? action.payload.hasRootKey : state - }, - loadFailure(state = null, action): Error | null { if (action.type === 'ACCOUNT_LOAD_FAILED') { const { error } = action.payload @@ -227,22 +225,21 @@ const accountInner = buildReducer({ }, login: memoizeReducer( - (next: AccountNext) => next.self.appId, + (next: AccountNext) => next.root.login.appId, (next: AccountNext) => next.self.loginTree, - (appId, loginTree): LoginTree => findAppLogin(loginTree, appId) + (appId, loginTree): LoginTree => { + const out = searchTree(loginTree, login => login.appId === appId) + if (out == null) { + throw new Error(`Internal error: cannot find login for ${appId}`) + } + return out + } ), - loginKey(state = new Uint8Array(0), action): Uint8Array { - return action.type === 'LOGIN' ? action.payload.loginKey : state - }, - loginTree: memoizeReducer( - (next: AccountNext) => next.self.appId, - (next: AccountNext) => next.self.loginKey, - (next: AccountNext) => next.self.hasRootKey, (next: AccountNext) => next.self.stashTree, - (appId, loginKey, hasRootKey, stashTree): LoginTree => - makeLoginTree(stashTree, loginKey, hasRootKey ? '' : appId) + (next: AccountNext) => next.self.sessionKey, + (stashTree, sessionKey): LoginTree => makeLoginTree(stashTree, sessionKey) ), loginType(state = 'newAccount', action): LoginType { @@ -253,6 +250,10 @@ const accountInner = buildReducer({ return action.type === 'LOGIN' ? action.payload.rootLoginId : state }, + sessionKey(state = blankSessionKey, action): SessionKey { + return action.type === 'LOGIN' ? action.payload.sessionKey : state + }, + stashTree: memoizeReducer( (next: AccountNext) => next.self.rootLoginId, (next: AccountNext) => next.root.login.stashes, diff --git a/src/core/account/lobby-api.ts b/src/core/account/lobby-api.ts index 730444249..a64f3be7a 100644 --- a/src/core/account/lobby-api.ts +++ b/src/core/account/lobby-api.ts @@ -6,10 +6,15 @@ import { EdgeLobby, EdgeLoginRequest } from '../../types/types' import { shuffle } from '../../util/shuffle' import { asLobbyLoginPayload } from '../login/edge' import { fetchLobbyRequest, sendLobbyReply } from '../login/lobby' -import { sanitizeLoginStash, syncLogin } from '../login/login' +import { + decryptChildKey, + sanitizeLoginStash, + searchTree, + syncLogin +} from '../login/login' import { getStashById } from '../login/login-selectors' import { ApiInput } from '../root-pixie' -import { ensureAccountExists, findAppLogin } from './account-init' +import { ensureAccountExists } from './account-init' const wasLobbyLoginPayload = uncleaner(asLobbyLoginPayload) @@ -70,29 +75,32 @@ async function approveLoginRequest( lobbyId: string, lobbyJson: EdgeLobbyRequest ): Promise { + const { sessionKey } = ai.props.state.accounts[accountId] + // For crash errors: ai.props.log.breadcrumb('approveLoginRequest', {}) - const { login, loginTree } = ai.props.state.accounts[accountId] - // Ensure that the login object & account repo exist: - await syncLogin(ai, loginTree, login) - - const newLoginTree = await ensureAccountExists(ai, loginTree, appId) - const requestedLogin = findAppLogin(newLoginTree, appId) - if (requestedLogin == null) { + await syncLogin(ai, sessionKey) + const { stashTree } = getStashById(ai, sessionKey.loginId) + + const newStashTree = await ensureAccountExists( + ai, + stashTree, + sessionKey, + appId + ) + const appStash = searchTree(newStashTree, stash => stash.appId === appId) + if (appStash == null) { throw new Error('Failed to create the requested login object') } - - // Create a sanitized login stash object: - const { stashTree } = getStashById(ai, loginTree.loginId) - const loginStash = sanitizeLoginStash(stashTree, appId) + const appKey = decryptChildKey(newStashTree, sessionKey, appStash.loginId) // Send the reply: const replyData = wasLobbyLoginPayload({ appId, - loginKey: requestedLogin.loginKey, - loginStash + loginKey: appKey.loginKey, + loginStash: sanitizeLoginStash(newStashTree, appId) }) await sendLobbyReply(ai, lobbyId, lobbyJson, replyData) let timeout: ReturnType | undefined @@ -105,13 +113,11 @@ async function approveLoginRequest( timeout = setTimeout(() => { timeout = undefined - syncLogin(ai, newLoginTree, requestedLogin) + syncLogin(ai, sessionKey) .then(() => { timeout = setTimeout(() => { timeout = undefined - syncLogin(ai, newLoginTree, requestedLogin).catch(error => - ai.props.onError(error) - ) + syncLogin(ai, sessionKey).catch(error => ai.props.onError(error)) }, 20000) }) .catch(error => ai.props.onError(error)) diff --git a/src/core/actions.ts b/src/core/actions.ts index 25b779708..8f8297004 100644 --- a/src/core/actions.ts +++ b/src/core/actions.ts @@ -21,7 +21,7 @@ import { TxidHashes } from './currency/wallet/currency-wallet-reducer' import { LoginStash } from './login/login-stash' -import { LoginType } from './login/login-types' +import { LoginType, SessionKey } from './login/login-types' import { StorageWalletState, StorageWalletStatus @@ -354,12 +354,10 @@ export type RootAction = // Fires when a user logs in. type: 'LOGIN' payload: { - appId: string - hasRootKey: boolean - loginKey: Uint8Array loginType: LoginType pauseWallets: boolean rootLoginId: Uint8Array + sessionKey: SessionKey } } | { diff --git a/src/core/context/context-api.ts b/src/core/context/context-api.ts index 549b962e1..7ef89a2e8 100644 --- a/src/core/context/context-api.ts +++ b/src/core/context/context-api.ts @@ -14,10 +14,10 @@ import { } from '../../types/types' import { verifyData } from '../../util/crypto/verify' import { base58 } from '../../util/encoding' -import { findAppLogin, makeAccount } from '../account/account-init' +import { makeAccount } from '../account/account-init' import { createLogin, usernameAvailable } from '../login/create' import { requestEdgeLogin } from '../login/edge' -import { makeLoginTree, syncLogin } from '../login/login' +import { makeAuthJson, searchTree, syncLogin } from '../login/login' import { loginFetch } from '../login/login-fetch' import { fetchLoginMessages } from '../login/login-messages' import { @@ -26,6 +26,7 @@ import { getStashByUsername } from '../login/login-selectors' import { removeStash, saveStash } from '../login/login-stash' +import { SessionKey } from '../login/login-types' import { resetOtp } from '../login/otp' import { loginPassword } from '../login/password' import { loginPin2 } from '../login/pin2' @@ -93,8 +94,8 @@ export function makeContextApi(ai: ApiInput): EdgeContext { if (opts.username != null) { opts.username = fixUsername(opts.username) } - const loginTree = await createLogin(ai, opts, opts) - return await makeAccount(ai, appId, loginTree, 'newAccount', opts) + const sessionKey = await createLogin(ai, opts, opts) + return await makeAccount(ai, sessionKey, 'newAccount', opts) }, async loginWithKey( @@ -111,16 +112,26 @@ export function makeContextApi(ai: ApiInput): EdgeContext { throw new Error('User does not exist on this device') } - const loginTree = makeLoginTree(stashTree, base58.parse(loginKey), appId) + const appStash = searchTree(stashTree, stash => stash.appId === appId) + if (appStash == null) { + throw new Error(`Cannot find requested appId: "${appId}"`) + } + const sessionKey: SessionKey = { + loginId: appStash.loginId, + loginKey: base58.parse(loginKey) + } + + // Verify that the provided key works for decryption: + makeAuthJson(stashTree, sessionKey) + + // Save the date: stashTree.lastLogin = now saveStash(ai, stashTree).catch(() => {}) // Since we logged in offline, update the stash in the background: - syncLogin(ai, loginTree, findAppLogin(loginTree, appId)).catch(error => - ai.props.onError(error) - ) + syncLogin(ai, sessionKey).catch(error => ai.props.onError(error)) - return await makeAccount(ai, appId, loginTree, 'keyLogin', opts) + return await makeAccount(ai, sessionKey, 'keyLogin', opts) }, async loginWithPassword( @@ -133,13 +144,13 @@ export function makeContextApi(ai: ApiInput): EdgeContext { username = fixUsername(username) const stashTree = getStashByUsername(ai, username) - const loginTree = await loginPassword( + const sessionKey = await loginPassword( ai, stashTree ?? getEmptyStash(username), password, opts ) - return await makeAccount(ai, appId, loginTree, 'passwordLogin', opts) + return await makeAccount(ai, sessionKey, 'passwordLogin', opts) }, checkPasswordRules, @@ -161,8 +172,8 @@ export function makeContextApi(ai: ApiInput): EdgeContext { throw new Error('User does not exist on this device') } - const loginTree = await loginPin2(ai, appId, stashTree, pin, opts) - return await makeAccount(ai, appId, loginTree, 'pinLogin', opts) + const sessionKey = await loginPin2(ai, appId, stashTree, pin, opts) + return await makeAccount(ai, sessionKey, 'pinLogin', opts) }, async loginWithRecovery2( @@ -176,14 +187,14 @@ export function makeContextApi(ai: ApiInput): EdgeContext { username = fixUsername(username) const stashTree = getStashByUsername(ai, username) - const loginTree = await loginRecovery2( + const sessionKey = await loginRecovery2( ai, stashTree ?? getEmptyStash(username), base58.parse(recovery2Key), answers, opts ) - return await makeAccount(ai, appId, loginTree, 'recoveryLogin', opts) + return await makeAccount(ai, sessionKey, 'recoveryLogin', opts) }, async fetchRecovery2Questions( diff --git a/src/core/login/create.ts b/src/core/login/create.ts index de2e09c46..6a025dd7c 100644 --- a/src/core/login/create.ts +++ b/src/core/login/create.ts @@ -12,7 +12,7 @@ import { loginFetch } from './login-fetch' import { makeSecretKit } from './login-secret' import { hashUsername } from './login-selectors' import { LoginStash, saveStash } from './login-stash' -import { LoginKit, LoginTree } from './login-types' +import { LoginKit, LoginTree, SessionKey } from './login-types' import { makeUsernameKit } from './login-username' import { makePasswordKit } from './password' import { makeChangePin2Kit } from './pin2' @@ -50,20 +50,21 @@ export async function usernameAvailable( */ export async function makeCreateKit( ai: ApiInput, - parentLogin: LoginTree | undefined, + parentSessionKey: SessionKey | undefined, appId: string, opts: LoginCreateOpts -): Promise { - // For crash errors: - ai.props.log.breadcrumb('makeCreateKit', {}) - +): Promise<{ kit: LoginKit; sessionKey: SessionKey }> { const { keyInfo, password, pin, username } = opts const { io } = ai.props + // For crash errors: + ai.props.log.breadcrumb('makeCreateKit', {}) + // Figure out login identity: - const isRoot = parentLogin == null + const isRoot = parentSessionKey == null const loginId = io.random(32) const loginKey = io.random(32) + const sessionKey = { loginId, loginKey } // Create the basic login object, but without any authentication methods: const login: LoginTree = { @@ -86,10 +87,10 @@ export async function makeCreateKit( // Set up optional login methods: if (keyInfo != null) { - keysKit = makeKeysKit(ai, login, [keyInfo]) + keysKit = makeKeysKit(ai, sessionKey, [keyInfo]) } - if (parentLogin != null) { - parentBox = encrypt(io, loginKey, parentLogin.loginKey) + if (parentSessionKey != null) { + parentBox = encrypt(io, loginKey, parentSessionKey.loginKey) } if (password != null && username != null) { passwordKit = await makePasswordKit(ai, login, username, password) @@ -102,18 +103,7 @@ export async function makeCreateKit( } // Bundle everything: - return { - login: { - appId, - loginId, - loginKey, - keyInfos: [], - ...keysKit?.login, - ...passwordKit?.login, - ...pin2Kit?.login, - ...secretKit.login, - ...usernameKit?.login - }, + const kit: LoginKit = { loginId, server: { ...wasCreateLoginPayload({ @@ -139,6 +129,7 @@ export async function makeCreateKit( ...usernameKit?.stash } } + return { kit, sessionKey } } /** @@ -148,17 +139,17 @@ export async function createLogin( ai: ApiInput, accountOpts: EdgeAccountOptions, opts: LoginCreateOpts -): Promise { +): Promise { const { challengeId, now = new Date() } = accountOpts // For crash errors: ai.props.log.breadcrumb('createLogin', {}) - const kit = await makeCreateKit(ai, undefined, '', opts) + const { kit, sessionKey } = await makeCreateKit(ai, undefined, '', opts) const request = { challengeId, data: kit.server } await loginFetch(ai, 'POST', kit.serverPath, request) kit.stash.lastLogin = now await saveStash(ai, kit.stash as LoginStash) - return kit.login as LoginTree + return sessionKey } diff --git a/src/core/login/edge.ts b/src/core/login/edge.ts index 41342a1b4..1d493d951 100644 --- a/src/core/login/edge.ts +++ b/src/core/login/edge.ts @@ -10,7 +10,7 @@ import { import { makeAccount } from '../account/account-init' import { ApiInput } from '../root-pixie' import { makeLobby } from './lobby' -import { makeLoginTree, searchTree, syncLogin } from './login' +import { searchTree, syncLogin } from './login' import { getStashById } from './login-selectors' import { asLoginStash, LoginStash, saveStash } from './login-stash' @@ -39,12 +39,12 @@ async function unpackAccount( appId: string, opts: EdgeAccountOptions ): Promise { - // For crash errors: - ai.props.log.breadcrumb('unpackAccount', {}) - const { now = new Date() } = opts const { loginKey, loginStash: stashTree } = payload + // For crash errors: + ai.props.log.breadcrumb('unpackAccount', {}) + // Find the appropriate child: const child = searchTree(stashTree, stash => stash.appId === appId) if (child == null) { @@ -62,13 +62,12 @@ async function unpackAccount( await saveStash(ai, stashTree) // This is almost guaranteed to blow up spectacularly: - const loginTree = makeLoginTree(stashTree, loginKey, appId) - const login = searchTree(loginTree, login => login.appId === appId) - if (login == null) { - throw new Error(`Cannot find requested appId: "${appId}"`) + const sessionKey = { + loginId: child.loginId, + loginKey } - const newLoginTree = await syncLogin(ai, loginTree, login) - return await makeAccount(ai, appId, newLoginTree, 'edgeLogin', opts) + await syncLogin(ai, sessionKey) + return await makeAccount(ai, sessionKey, 'edgeLogin', opts) } /** diff --git a/src/core/login/keys.ts b/src/core/login/keys.ts index 649890957..cf0e8f32e 100644 --- a/src/core/login/keys.ts +++ b/src/core/login/keys.ts @@ -23,6 +23,7 @@ import { asEdgeWalletInfo, LoginKit, LoginTree, + SessionKey, wasEdgeWalletInfo } from './login-types' import { @@ -68,7 +69,7 @@ export function makeKeyInfo( */ export function makeKeysKit( ai: ApiInput, - login: LoginTree, + sessionKey: SessionKey, keyInfos: EdgeWalletInfo[] ): LoginKit { // For crash errors: @@ -79,7 +80,7 @@ export function makeKeysKit( encrypt( io, utf8.parse(JSON.stringify(wasEdgeWalletInfo(info))), - login.loginKey + sessionKey.loginKey ) ) @@ -91,8 +92,7 @@ export function makeKeysKit( } return { - login: { keyInfos }, - loginId: login.loginId, + loginId: sessionKey.loginId, server: wasCreateKeysPayload({ keyBoxes, newSyncKeys }), serverPath: '/v2/login/keys', stash: { keyBoxes } diff --git a/src/core/login/login-secret.ts b/src/core/login/login-secret.ts index 17f7fe27a..ced6a1225 100644 --- a/src/core/login/login-secret.ts +++ b/src/core/login/login-secret.ts @@ -14,9 +14,6 @@ export function makeSecretKit( const loginAuthBox = encrypt(io, loginAuth, loginKey) return { - login: { - loginAuth - }, loginId, server: wasChangeSecretPayload({ loginAuth, diff --git a/src/core/login/login-selectors.ts b/src/core/login/login-selectors.ts index 0753c2317..dace06415 100644 --- a/src/core/login/login-selectors.ts +++ b/src/core/login/login-selectors.ts @@ -37,7 +37,18 @@ export function getStashById(ai: ApiInput, loginId: Uint8Array): StashLeaf { ) if (stash != null) return { stashTree, stash } } - throw new Error(`Cannot find stash ${base64.stringify(loginId)}`) + throw new Error(`Cannot find stash '${base64.stringify(loginId)}'`) +} + +export function getChildStash( + stashTree: LoginStash, + loginId: Uint8Array +): LoginStash { + const stash = searchTree(stashTree, stash => + verifyData(stash.loginId, loginId) + ) + if (stash != null) return stash + throw new Error(`Cannot find child stash '${base64.stringify(loginId)}'`) } // Hashed username cache: diff --git a/src/core/login/login-types.ts b/src/core/login/login-types.ts index 52960910b..b36dfe6b5 100644 --- a/src/core/login/login-types.ts +++ b/src/core/login/login-types.ts @@ -8,6 +8,17 @@ import { import { asJsonObject } from '../../util/file-helpers' import { LoginStash } from './login-stash' +/** + * A key that decrypts a login stash. + */ +export interface SessionKey { + /** The login that this key belongs to. This may be a child login. */ + loginId: Uint8Array + + /** The decryption key. */ + loginKey: Uint8Array +} + // Login data decrypted into memory. export interface LoginTree { isRoot: boolean @@ -57,12 +68,6 @@ export interface LoginKit { /** The change will affect the node with this ID. */ loginId: Uint8Array - /** - * A diff to apply to the login tree, starting at the `loginId` node. - * We want to eliminate this, and simply decrypt the stash. - */ - login: Partial - /** * The login-server payload that achieves the change. * Not all routes take a payload, such as the DELETE routes. diff --git a/src/core/login/login-username.ts b/src/core/login/login-username.ts index 51d41f671..720b4ad66 100644 --- a/src/core/login/login-username.ts +++ b/src/core/login/login-username.ts @@ -69,12 +69,6 @@ export async function makeChangeUsernameKit( } return { - login: { - ...passwordKit?.login, - ...pin2Kit?.login, - ...recovery2Kit?.login, - ...usernameKit?.login - }, loginId, server: { ...passwordKit?.server, @@ -107,7 +101,6 @@ export async function makeUsernameKit( const userId = await hashUsername(ai, username) return { - login: { userId, username }, loginId, server: wasChangeUsernamePayload({ userId, diff --git a/src/core/login/login.ts b/src/core/login/login.ts index 701e386ff..41dc66673 100644 --- a/src/core/login/login.ts +++ b/src/core/login/login.ts @@ -9,16 +9,17 @@ import { asLoginPayload } from '../../types/server-cleaners' import { LoginPayload, LoginRequestBody } from '../../types/server-types' import { asMaybeOtpError, EdgeAccountOptions } from '../../types/types' import { decrypt, decryptText } from '../../util/crypto/crypto' +import { totp } from '../../util/crypto/hotp' import { verifyData } from '../../util/crypto/verify' import { softCat } from '../../util/util' import { ApiInput } from '../root-pixie' -import { decryptKeyInfos, mergeKeyInfos } from './keys' +import { decryptKeyInfos } from './keys' import { loginFetch } from './login-fetch' import { makeSecretKit } from './login-secret' -import { getStashById } from './login-selectors' +import { getChildStash, getStashById } from './login-selectors' import { LoginStash, saveStash } from './login-stash' -import { LoginKit, LoginTree } from './login-types' -import { getLoginOtp, getStashOtp } from './otp' +import { LoginKit, LoginTree, SessionKey } from './login-types' +import { getStashOtp } from './otp' /** * Returns the login that satisfies the given predicate, @@ -262,13 +263,12 @@ function makeLoginTreeInner( */ export function makeLoginTree( stashTree: LoginStash, - loginKey: Uint8Array, - appId: string = '' + sessionKey: SessionKey ): LoginTree { return updateTree( stashTree, - stash => stash.appId === appId, - stash => makeLoginTreeInner(stash, loginKey), + stash => verifyData(stash.loginId, sessionKey.loginId), + stash => makeLoginTreeInner(stash, sessionKey.loginKey), (stash, children): LoginTree => { const { appId, @@ -339,7 +339,7 @@ export async function serverLogin( opts: EdgeAccountOptions, serverAuth: LoginRequestBody, decrypt: (reply: LoginPayload) => Promise -): Promise { +): Promise { const { now = new Date() } = opts const { deviceDescription } = ai.props.state.login @@ -380,6 +380,7 @@ export async function serverLogin( ) // Try decrypting the reply: + const { loginId } = loginReply const loginKey = await decrypt(loginReply) // Save the latest data: @@ -389,7 +390,6 @@ export async function serverLogin( // Ensure the account has secret-key login enabled: if (loginReply.loginAuthBox == null) { - const { loginId } = loginReply const { stash, stashTree } = getStashById(ai, loginId) const secretKit = makeSecretKit(ai, { loginId, loginKey }) const request: LoginRequestBody = { @@ -403,7 +403,7 @@ export async function serverLogin( await saveStash(ai, applyLoginPayload(stashTree, loginKey, loginReply)) } - return makeLoginTree(stashTree, loginKey, stash.appId) + return { loginId, loginKey } } /** @@ -413,35 +413,21 @@ export async function serverLogin( */ export async function applyKit( ai: ApiInput, - loginTree: LoginTree, + sessionKey: SessionKey, kit: LoginKit -): Promise { - const { loginId, serverMethod = 'POST', serverPath } = kit - const login = searchTree(loginTree, login => - verifyData(login.loginId, loginId) - ) - if (login == null) throw new Error('Cannot apply kit: missing login') +): Promise { + const { serverMethod = 'POST', serverPath } = kit - const { stashTree } = getStashById(ai, loginId) - const request = makeAuthJson(stashTree, login) + const { stashTree } = getStashById(ai, kit.loginId) + const childKey = decryptChildKey(stashTree, sessionKey, kit.loginId) + const request = makeAuthJson(stashTree, childKey) request.data = kit.server await loginFetch(ai, serverMethod, serverPath, request) - const newLoginTree = updateTree( - loginTree, - login => verifyData(login.loginId, loginId), - login => ({ - ...login, - ...kit.login, - children: softCat(login.children, kit.login.children), - keyInfos: mergeKeyInfos(softCat(login.keyInfos, kit.login.keyInfos)) - }), - (login, children) => ({ ...login, children }) - ) - const newStashTree = updateTree( + const newStashTree = updateTree( stashTree, - stash => verifyData(stash.loginId, loginId), - (stash): LoginStash => ({ + stash => verifyData(stash.loginId, kit.loginId), + stash => ({ ...stash, ...kit.stash, children: softCat(stash.children, kit.stash.children), @@ -451,7 +437,7 @@ export async function applyKit( ) await saveStash(ai, newStashTree) - return newLoginTree + return newStashTree } /** @@ -462,12 +448,12 @@ export async function applyKit( */ export async function applyKits( ai: ApiInput, - loginTree: LoginTree, + sessionKey: SessionKey, kits: Array ): Promise { for (const kit of kits) { if (kit == null) continue - await applyKit(ai, loginTree, kit) + await applyKit(ai, sessionKey, kit) } } @@ -476,10 +462,9 @@ export async function applyKits( */ export async function syncLogin( ai: ApiInput, - loginTree: LoginTree, - login: LoginTree -): Promise { - const { stashTree, stash } = getStashById(ai, login.loginId) + sessionKey: SessionKey +): Promise { + const { stashTree, stash } = getStashById(ai, sessionKey.loginId) // First, hit the fast endpoint to see if we even need to sync: const { syncToken } = stash @@ -489,54 +474,109 @@ export async function syncLogin( loginId: stash.loginId, syncToken }) - if (asBoolean(reply)) return loginTree + if (asBoolean(reply)) return } catch (error) { // We can fall back on a full sync if we fail here. } } // If we do need to sync, prepare for a full login: - const request = makeAuthJson(stashTree, login) + const request = makeAuthJson(stashTree, sessionKey) const opts: EdgeAccountOptions = { // Avoid updating the lastLogin date: now: stashTree.lastLogin } - return await serverLogin(ai, stashTree, stash, opts, request, async () => { - return login.loginKey + await serverLogin(ai, stashTree, stash, opts, request, async () => { + return sessionKey.loginKey }) } +/** + * Finds the session key for a child login. + */ +export function decryptChildKey( + stashTree: LoginStash, + sessionKey: SessionKey, + loginId: Uint8Array +): SessionKey { + function searchChildren( + children: LoginStash[], + loginKey: Uint8Array + ): SessionKey | undefined { + for (const child of children) { + // This will never happen, but TypeScript doesn't know that: + if (child.parentBox == null) continue + + // If this is the right one, return it: + if (verifyData(child.loginId, loginId)) { + return { + loginId: child.loginId, + loginKey: decrypt(child.parentBox, loginKey) + } + } + + // We can skip the next decryption if there are no children: + const { children = [] } = child + if (children.length === 0) continue + + // Otherwise, we need to decrypt the child's key, and recurse in: + const out = searchChildren(children, decrypt(child.parentBox, loginKey)) + if (out != null) return out + } + } + + // If this is already the right session key, do nothing: + if (verifyData(loginId, sessionKey.loginId)) return sessionKey + + // Find the stash this key goes with: + const stash = getChildStash(stashTree, sessionKey.loginId) + + // Recurse into its children: + const out = searchChildren(stash?.children ?? [], sessionKey.loginKey) + if (out == null) { + throw new Error( + `Cannot decrypt child login '${base64.stringify(sessionKey.loginId)}'` + ) + } + return out +} + /** * Sets up a login v2 server authorization JSON. */ export function makeAuthJson( stashTree: LoginStash, - login: LoginTree + sessionKey: SessionKey ): LoginRequestBody { - const stash = searchTree(stashTree, stash => stash.appId === login.appId) - const { syncToken, voucherAuth, voucherId } = stash ?? { - syncToken: undefined, - voucherAuth: undefined, - voucherId: undefined - } + const stash = getChildStash(stashTree, sessionKey.loginId) - const { loginId, userId, loginAuth, passwordAuth } = login - if (loginAuth != null) { + const { + loginAuthBox, + otpKey, + passwordAuthBox, + syncToken, + userId, + voucherAuth, + voucherId + } = stash + const otp = otpKey != null ? totp(otpKey) : undefined + + if (loginAuthBox != null) { return { - loginId, - loginAuth, - otp: getLoginOtp(login), + loginAuth: decrypt(loginAuthBox, sessionKey.loginKey), + loginId: sessionKey.loginId, + otp, syncToken, voucherAuth, voucherId } } - if (passwordAuth != null && userId != null) { + if (passwordAuthBox != null && userId != null) { return { + passwordAuth: decrypt(passwordAuthBox, sessionKey.loginKey), userId, - passwordAuth, - otp: getLoginOtp(login), + otp, syncToken, voucherAuth, voucherId diff --git a/src/core/login/otp.ts b/src/core/login/otp.ts index 058a1b12c..58067f23c 100644 --- a/src/core/login/otp.ts +++ b/src/core/login/otp.ts @@ -38,16 +38,11 @@ export async function enableOtp( accountId: string, otpTimeout: number ): Promise { - const { loginTree } = ai.props.state.accounts[accountId] + const { loginTree, sessionKey } = ai.props.state.accounts[accountId] const { otpKey = ai.props.io.random(10) } = loginTree const kit: LoginKit = { loginId: loginTree.loginId, - login: { - otpKey, - otpResetDate: undefined, - otpTimeout - }, server: wasChangeOtpPayload({ otpKey, otpTimeout @@ -59,21 +54,16 @@ export async function enableOtp( otpTimeout } } - await applyKit(ai, loginTree, kit) + await applyKit(ai, sessionKey, kit) } export async function disableOtp( ai: ApiInput, accountId: string ): Promise { - const { loginTree } = ai.props.state.accounts[accountId] + const { loginTree, sessionKey } = ai.props.state.accounts[accountId] const kit: LoginKit = { - login: { - otpKey: undefined, - otpResetDate: undefined, - otpTimeout: undefined - }, loginId: loginTree.loginId, server: undefined, serverMethod: 'DELETE', @@ -84,23 +74,20 @@ export async function disableOtp( otpTimeout: undefined } } - await applyKit(ai, loginTree, kit) + await applyKit(ai, sessionKey, kit) } export async function cancelOtpReset( ai: ApiInput, accountId: string ): Promise { - const { loginTree } = ai.props.state.accounts[accountId] + const { loginTree, sessionKey } = ai.props.state.accounts[accountId] const { otpTimeout, otpKey } = loginTree if (otpTimeout == null || otpKey == null) { throw new Error('Cannot cancel 2FA reset: 2FA is not enabled.') } const kit: LoginKit = { - login: { - otpResetDate: undefined - }, loginId: loginTree.loginId, server: wasChangeOtpPayload({ otpTimeout, @@ -111,7 +98,7 @@ export async function cancelOtpReset( otpResetDate: undefined } } - await applyKit(ai, loginTree, kit) + await applyKit(ai, sessionKey, kit) } /** diff --git a/src/core/login/password.ts b/src/core/login/password.ts index 3b724a74b..7b3062253 100644 --- a/src/core/login/password.ts +++ b/src/core/login/password.ts @@ -3,10 +3,10 @@ import { EdgeAccountOptions } from '../../types/types' import { decrypt, encrypt } from '../../util/crypto/crypto' import { ApiInput } from '../root-pixie' import { makeSnrp, scrypt, userIdSnrp } from '../scrypt/scrypt-selectors' -import { applyKit, makeLoginTree, serverLogin, syncLogin } from './login' +import { applyKit, serverLogin, syncLogin } from './login' import { hashUsername } from './login-selectors' import { LoginStash, saveStash } from './login-stash' -import { LoginKit, LoginTree } from './login-types' +import { LoginKit, LoginTree, SessionKey } from './login-types' const passwordAuthSnrp = userIdSnrp @@ -22,7 +22,7 @@ async function loginPasswordOffline( stashTree: LoginStash, password: string, opts: EdgeAccountOptions -): Promise { +): Promise { const { now = new Date() } = opts const { passwordBox, passwordKeySnrp, username } = stashTree @@ -31,17 +31,21 @@ async function loginPasswordOffline( } const up = makeHashInput(username, password) const passwordKey = await scrypt(ai, up, passwordKeySnrp) - const loginKey = decrypt(passwordBox, passwordKey) - const loginTree = makeLoginTree(stashTree, loginKey) + const sessionKey = { + loginId: stashTree.loginId, + loginKey: decrypt(passwordBox, passwordKey) + } + + // Save the date: stashTree.lastLogin = now saveStash(ai, stashTree).catch(() => {}) // Since we logged in offline, update the stash in the background: // TODO: If the user provides an OTP token, add that to the stash. const { log } = ai.props - syncLogin(ai, loginTree, loginTree).catch(error => log.error(error)) + syncLogin(ai, sessionKey).catch(error => log.error(error)) - return loginTree + return sessionKey } /** @@ -52,7 +56,7 @@ async function loginPasswordOnline( stashTree: LoginStash, password: string, opts: EdgeAccountOptions -): Promise { +): Promise { const { username } = stashTree if (username == null) throw new Error('Password login requires a username') @@ -98,7 +102,7 @@ export async function loginPassword( stashTree: LoginStash, password: string, opts: EdgeAccountOptions -): Promise { +): Promise { return await loginPasswordOffline(ai, stashTree, password, opts).catch(() => loginPasswordOnline(ai, stashTree, password, opts) ) @@ -110,12 +114,12 @@ export async function changePassword( password: string ): Promise { const accountState = ai.props.state.accounts[accountId] - const { loginTree } = accountState + const { loginTree, sessionKey } = accountState const { username } = accountState.stashTree if (username == null) throw new Error('Password login requires a username') const kit = await makePasswordKit(ai, loginTree, username, password) - await applyKit(ai, loginTree, kit) + await applyKit(ai, sessionKey, kit) } /** @@ -145,12 +149,9 @@ export async function deletePassword( ai: ApiInput, accountId: string ): Promise { - const { loginTree } = ai.props.state.accounts[accountId] + const { loginTree, sessionKey } = ai.props.state.accounts[accountId] const kit: LoginKit = { - login: { - passwordAuth: undefined - }, loginId: loginTree.loginId, server: undefined, serverMethod: 'DELETE', @@ -164,9 +165,8 @@ export async function deletePassword( // Only remove `passwordAuth` if we have another way to get in: if (loginTree.loginAuth != null) { kit.stash.passwordAuthBox = undefined - kit.login.passwordAuth = undefined } - await applyKit(ai, loginTree, kit) + await applyKit(ai, sessionKey, kit) } /** @@ -198,9 +198,6 @@ export async function makePasswordKit( ]) return { - login: { - passwordAuth - }, loginId: login.loginId, server: wasChangePasswordPayload({ passwordAuth, diff --git a/src/core/login/pin2.ts b/src/core/login/pin2.ts index 5210ed254..7e9c37de1 100644 --- a/src/core/login/pin2.ts +++ b/src/core/login/pin2.ts @@ -12,7 +12,7 @@ import { applyKits, searchTree, serverLogin } from './login' import { loginFetch } from './login-fetch' import { getStashById } from './login-selectors' import { LoginStash } from './login-stash' -import { LoginKit, LoginTree } from './login-types' +import { LoginKit, LoginTree, SessionKey } from './login-types' import { getLoginOtp } from './otp' function makePin2Id( @@ -49,7 +49,7 @@ export async function loginPin2( stashTree: LoginStash, pin: string, opts: EdgeAccountOptions -): Promise { +): Promise { const stash = findPin2Stash(stashTree, appId) if (stash == null || stash.pin2Key == null) { throw new Error('PIN login is not enabled for this account on this device') @@ -176,7 +176,6 @@ export function makeChangePin2IdKit( if (pin2Key == null) return return { - login: {}, loginId, server: wasChangePin2IdPayload({ pin2Id: makePin2Id(pin2Key, newUsername) @@ -205,10 +204,6 @@ export function makeChangePin2Kit( const pin2KeyBox = encrypt(io, pin2Key, loginKey) return { - login: { - pin2Key, - pin - }, loginId, server: wasChangePin2Payload({ pin2Id: makePin2Id(pin2Key, username), @@ -225,10 +220,6 @@ export function makeChangePin2Kit( } } else { return { - login: { - pin2Key: undefined, - pin - }, loginId: login.loginId, server: wasChangePin2Payload({ pin2Id: undefined, @@ -264,9 +255,6 @@ export function makeDeletePin2Kits(loginTree: LoginTree): LoginKit[] { */ export function makeDeletePin2Kit(login: LoginTree): LoginKit { return { - login: { - pin2Key: undefined - }, loginId: login.loginId, server: undefined, serverMethod: 'DELETE', diff --git a/src/core/login/recovery2.ts b/src/core/login/recovery2.ts index 1b693ce5b..40612561e 100644 --- a/src/core/login/recovery2.ts +++ b/src/core/login/recovery2.ts @@ -13,7 +13,7 @@ import { ApiInput } from '../root-pixie' import { applyKit, serverLogin } from './login' import { loginFetch } from './login-fetch' import { LoginStash } from './login-stash' -import { LoginKit, LoginTree } from './login-types' +import { LoginKit, LoginTree, SessionKey } from './login-types' function makeRecovery2Id( recovery2Key: Uint8Array, @@ -41,7 +41,7 @@ export async function loginRecovery2( recovery2Key: Uint8Array, answers: string[], opts: EdgeAccountOptions -): Promise { +): Promise { const { username } = stashTree if (username == null) throw new Error('Recovery login requires a username') @@ -97,19 +97,19 @@ export async function changeRecovery( answers: string[] ): Promise { const accountState = ai.props.state.accounts[accountId] - const { loginTree } = accountState + const { loginTree, sessionKey } = accountState const { username } = accountState.stashTree if (username == null) throw new Error('Recovery login requires a username') const kit = makeRecovery2Kit(ai, loginTree, username, questions, answers) - await applyKit(ai, loginTree, kit) + await applyKit(ai, sessionKey, kit) } export async function deleteRecovery( ai: ApiInput, accountId: string ): Promise { - const { loginTree } = ai.props.state.accounts[accountId] + const { loginTree, sessionKey } = ai.props.state.accounts[accountId] const kit = { login: { @@ -123,7 +123,7 @@ export async function deleteRecovery( recovery2Key: undefined } } - await applyKit(ai, loginTree, kit) + await applyKit(ai, sessionKey, kit) } /** @@ -138,7 +138,6 @@ export function makeChangeRecovery2IdKit( if (recovery2Key == null) return return { - login: {}, loginId, server: wasChangeRecovery2IdPayload({ recovery2Id: makeRecovery2Id(recovery2Key, newUsername) @@ -176,9 +175,6 @@ export function makeRecovery2Kit( const recovery2KeyBox = encrypt(io, recovery2Key, loginKey) return { - login: { - recovery2Key - }, loginId, server: wasChangeRecovery2Payload({ recovery2Id: makeRecovery2Id(recovery2Key, username), diff --git a/src/core/login/splitting.ts b/src/core/login/splitting.ts index 78b1e858a..415956a9e 100644 --- a/src/core/login/splitting.ts +++ b/src/core/login/splitting.ts @@ -106,7 +106,7 @@ export async function splitWalletInfo( newWalletType: string ): Promise { const accountState = ai.props.state.accounts[accountId] - const { allWalletInfosFull, login, loginTree } = accountState + const { allWalletInfosFull, sessionKey } = accountState // Find the wallet we are going to split: const walletInfo = allWalletInfosFull.find( @@ -157,8 +157,8 @@ export async function splitWalletInfo( } // Add the keys to the login: - const kit = makeKeysKit(ai, login, [newWalletInfo]) - await applyKit(ai, loginTree, kit) + const kit = makeKeysKit(ai, sessionKey, [newWalletInfo]) + await applyKit(ai, sessionKey, kit) // Try to copy metadata on a best-effort basis. // In the future we should clone the repo instead: