UNPKG

edge-core-js

Version:

Edge account & wallet management library

214 lines (146 loc) 4.08 kB
import { asArray, asCodec, asDate, asNumber, asObject, asOptional, asString, uncleaner } from 'cleaners' import { justFiles } from 'disklet' import { base64 } from 'rfc4648' import { fixUsername } from '../../client-side' import { asBase32, asBase64, asEdgeBox, asEdgeKeyBox, asEdgePendingVoucher, asEdgeSnrp } from '../../types/server-cleaners' import { verifyData } from '../../util/crypto/verify' import { base58 } from '../../util/encoding' import { searchTree } from './login' /** * The login data we store on disk. */ /** * Reads all login stashes from disk. */ export async function loadStashes( disklet, log ) { const out = [] const paths = await disklet.list('logins').then(justFiles) for (const path of paths) { try { out.push(asLoginStash(JSON.parse(await disklet.getText(path)))) } catch (error) { log.error(`Could not load ${path}: ${String(error)}`) } } return out } /** * Removes any login stash that may be stored for the given username. */ export async function removeStash( ai, loginId ) { const { dispatch, io } = ai.props const paths = await io.disklet.list('logins').then(justFiles) for (const path of paths) { try { const stash = asLoginStash(JSON.parse(await io.disklet.getText(path))) if (verifyData(stash.loginId, loginId)) await io.disklet.delete(path) } catch (error) {} } dispatch({ type: 'LOGIN_STASH_DELETED', payload: loginId }) } /** * Saves a login stash tree to disk. */ export async function saveStash( ai, stashTree ) { const { dispatch, io } = ai.props const { appId, loginId } = stashTree if (appId !== '') { throw new Error('Cannot save a login without an appId.') } if (loginId == null || loginId.length !== 32) { throw new Error('Invalid loginId') } await io.disklet.setText( `logins/${base58.stringify(loginId)}.json`, JSON.stringify(wasLoginStash(stashTree)) ) dispatch({ type: 'LOGIN_STASH_SAVED', payload: stashTree }) } const asUsername = raw => fixUsername(asString(raw)) export const asLoginStash = asObject({ // Identity: appId: asString, created: asOptional(asDate), lastLogin: asOptional(asDate), loginId: asBase64, // 2-factor: otpKey: asOptional(asBase32), otpResetDate: asOptional(asDate), otpTimeout: asOptional(asNumber), pendingVouchers: asOptional(asArray(asEdgePendingVoucher), () => []), voucherId: asOptional(asString), voucherAuth: asOptional(asBase64), // Return logins: loginAuthBox: asOptional(asEdgeBox), parentBox: asOptional(asEdgeBox), // Password login: passwordAuthBox: asOptional(asEdgeBox), passwordAuthSnrp: asOptional(asEdgeSnrp), passwordBox: asOptional(asEdgeBox), passwordKeySnrp: asOptional(asEdgeSnrp), // PIN v2 login: pin2Key: asOptional( asCodec( // Legacy Airbitz can wrongly send this in base58 for Edge login: raw => { const clean = asString(raw) return raw.slice(-1) !== '=' ? base58.parse(clean) : base64.parse(clean) }, clean => base64.stringify(clean) ) ), pin2TextBox: asOptional(asEdgeBox), // Recovery v2 login: recovery2Key: asOptional(asBase64), // Username: userId: asOptional(asBase64), username: asOptional(asUsername), // Keys and assorted goodies: children: asOptional(asArray(raw => asLoginStash(raw))), keyBoxes: asOptional(asArray(asEdgeKeyBox)), mnemonicBox: asOptional(asEdgeBox), rootKeyBox: asOptional(asEdgeBox), syncKeyBox: asOptional(asEdgeBox) }) export const wasLoginStash = uncleaner(asLoginStash) /** * Returns the duress stash nested within a given LoginStash tree. * This will return the duress stash even if the stashTree is the duress stash * itself. */ export function findDuressStash( stashTree, appId ) { const duressAppId = appId.endsWith('.duress') ? appId : appId + '.duress' if (stashTree.appId === duressAppId) return stashTree return searchTree(stashTree, stash => stash.appId === duressAppId) }