diff --git a/package.json b/package.json index 26b6e59e..2b0b41ec 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@ledgerhq/hw-transport": "^4.12.0", "@ledgerhq/hw-transport-node-hid": "^4.12.0", "@ledgerhq/ledger-core": "^1.3.0", - "@ledgerhq/live-common": "2.15.0", + "@ledgerhq/live-common": "2.16.1", "axios": "^0.18.0", "babel-runtime": "^6.26.0", "bcryptjs": "^2.4.3", diff --git a/src/commands/index.js b/src/commands/index.js index 1eac723b..87da444c 100644 --- a/src/commands/index.js +++ b/src/commands/index.js @@ -2,46 +2,48 @@ import type { Command } from 'helpers/ipc' -import getMemInfo from 'commands/getMemInfo' -import libcoreScanAccounts from 'commands/libcoreScanAccounts' -import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' import getAddress from 'commands/getAddress' -import signTransaction from 'commands/signTransaction' import getDeviceInfo from 'commands/getDeviceInfo' import getFirmwareInfo from 'commands/getFirmwareInfo' import getIsGenuine from 'commands/getIsGenuine' import getLatestFirmwareForDevice from 'commands/getLatestFirmwareForDevice' +import getMemInfo from 'commands/getMemInfo' import installApp from 'commands/installApp' -import listenDevices from 'commands/listenDevices' -import uninstallApp from 'commands/uninstallApp' -import installOsuFirmware from 'commands/installOsuFirmware' import installFinalFirmware from 'commands/installFinalFirmware' import installMcu from 'commands/installMcu' +import installOsuFirmware from 'commands/installOsuFirmware' +import isCurrencyAppOpened from 'commands/isCurrencyAppOpened' +import libcoreScanAccounts from 'commands/libcoreScanAccounts' +import libcoreSignAndBroadcast from 'commands/libcoreSignAndBroadcast' import listApps from 'commands/listApps' +import listenDevices from 'commands/listenDevices' +import signTransaction from 'commands/signTransaction' import testApdu from 'commands/testApdu' -import testInterval from 'commands/testInterval' import testCrash from 'commands/testCrash' +import testInterval from 'commands/testInterval' +import uninstallApp from 'commands/uninstallApp' const all: Array> = [ - getMemInfo, - libcoreScanAccounts, - libcoreSignAndBroadcast, getAddress, - signTransaction, getDeviceInfo, getFirmwareInfo, getIsGenuine, getLatestFirmwareForDevice, + getMemInfo, installApp, - listenDevices, - uninstallApp, - installOsuFirmware, installFinalFirmware, installMcu, + installOsuFirmware, + isCurrencyAppOpened, + libcoreScanAccounts, + libcoreSignAndBroadcast, listApps, + listenDevices, + signTransaction, testApdu, - testInterval, testCrash, + testInterval, + uninstallApp, ] all.forEach(cmd => { diff --git a/src/commands/isCurrencyAppOpened.js b/src/commands/isCurrencyAppOpened.js new file mode 100644 index 00000000..378c9467 --- /dev/null +++ b/src/commands/isCurrencyAppOpened.js @@ -0,0 +1,54 @@ +// @flow + +import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies' + +import { createCommand, Command } from 'helpers/ipc' +import { fromPromise } from 'rxjs/observable/fromPromise' + +import { withDevice } from 'helpers/deviceAccess' +import getBitcoinLikeInfo from 'helpers/devices/getBitcoinLikeInfo' +import getAddress from 'helpers/getAddressForCurrency' +import { standardDerivation } from 'helpers/derivations' + +type Input = { + devicePath: string, + currencyId: string, +} + +type Result = boolean + +const cmd: Command = createCommand( + 'isCurrencyAppOpened', + ({ devicePath, currencyId }) => + fromPromise( + withDevice(devicePath)(async transport => { + const currency = getCryptoCurrencyById(currencyId) + + // First, we check if the app can derivates on the currency + try { + await getAddress(currencyId)( + transport, + currencyId, + standardDerivation({ currency, segwit: false, x: 0 }), + { segwit: false }, + ) + + // then, just in case of BTC, we need to make sure we are on the correct BTC fork + const { bitcoinLikeInfo } = currency + if (bitcoinLikeInfo) { + const { P2SH, P2PKH } = await getBitcoinLikeInfo(transport) + return P2SH === bitcoinLikeInfo.P2SH && P2PKH === bitcoinLikeInfo.P2PKH + } + + // in case of ETH / XRP, the address derivation is enough + return true + } catch (e) { + console.log(e) + // if anything failed, it does not pass + return false + } + }), + ), +) + +export default cmd diff --git a/src/components/EnsureDeviceApp/index.js b/src/components/EnsureDeviceApp/index.js index 1e6690d7..d7ead84f 100644 --- a/src/components/EnsureDeviceApp/index.js +++ b/src/components/EnsureDeviceApp/index.js @@ -1,7 +1,6 @@ // @flow import { PureComponent } from 'react' import { connect } from 'react-redux' -import { standardDerivation } from 'helpers/derivations' import type { Account, CryptoCurrency } from '@ledgerhq/live-common/lib/types' import type { Device } from 'types/common' @@ -9,6 +8,7 @@ import type { Device } from 'types/common' import { getDevices } from 'reducers/devices' import type { State as StoreState } from 'reducers/index' import getAddress from 'commands/getAddress' +import isCurrencyAppOpened from 'commands/isCurrencyAppOpened' type OwnProps = { currency?: ?CryptoCurrency, @@ -50,6 +50,8 @@ const mapStateToProps = (state: StoreState) => ({ devices: getDevices(state), }) +// TODO we want to split into and +// and minimize the current codebase AF class EnsureDeviceApp extends PureComponent { state = { appStatus: 'progress', @@ -104,39 +106,39 @@ class EnsureDeviceApp extends PureComponent { return } - let appOptions - - if (account) { - appOptions = { - devicePath: deviceSelected.path, - currencyId: account.currency.id, - path: account.freshAddressPath, - accountAddress: account.freshAddress, - segwit: !!account.isSegwit, - } - } else if (currency) { - appOptions = { - devicePath: deviceSelected.path, - currencyId: currency.id, - path: standardDerivation({ currency, x: 0, segwit: false }), - } - } - try { - if (appOptions) { - const { address } = await getAddress.send(appOptions).toPromise() - if (account && account.freshAddress !== address) { - console.log(account) - console.warn(account.freshAddress, address) + if (account) { + const { address } = await getAddress + .send({ + devicePath: deviceSelected.path, + currencyId: account.currency.id, + path: account.freshAddressPath, + segwit: !!account.isSegwit, + }) + .toPromise() + const { freshAddress } = account + if (account && freshAddress !== address) { + console.warn({ freshAddress, address }) throw new Error('Account address is different than device address') } + } else if (currency) { + const pass = await isCurrencyAppOpened + .send({ + devicePath: deviceSelected.path, + currencyId: currency.id, + }) + .toPromise() + if (!pass) { + throw new Error(`${currency.name} app is not opened on the device`) + } } else { // TODO: real check if user is on the device dashboard if (!deviceSelected) { throw new Error('No device') } - await sleep(1) + await sleep(1) // WTF } + this.handleStatusChange(this.state.deviceStatus, 'success') if (withGenuineCheck && appStatus !== 'success') { diff --git a/src/helpers/devices/getBitcoinLikeInfo.js b/src/helpers/devices/getBitcoinLikeInfo.js new file mode 100644 index 00000000..82e8ed75 --- /dev/null +++ b/src/helpers/devices/getBitcoinLikeInfo.js @@ -0,0 +1,21 @@ +// @flow + +import type Transport from '@ledgerhq/hw-transport' + +const getBitcoinLikeInfo = ( + transport: Transport, +): Promise<{ + P2PKH: number, + P2SH: number, + message: Buffer, + short: Buffer, +}> => + transport.send(0xe0, 0x16, 0x00, 0x00).then(res => { + const P2PKH = res.readUInt16BE(0) + const P2SH = res.readUInt16BE(2) + const message = res.slice(5, res.readUInt8(4)) + const short = res.slice(5 + message.length + 1, res.readUInt8(5 + message.length)) + return { P2PKH, P2SH, message, short } + }) + +export default getBitcoinLikeInfo diff --git a/yarn.lock b/yarn.lock index ed92728a..ca0637af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1464,9 +1464,9 @@ npm "^5.7.1" prebuild-install "^2.2.2" -"@ledgerhq/live-common@2.15.0": - version "2.15.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-2.15.0.tgz#9677c223446c996e5d5f13658fc6af09e966c701" +"@ledgerhq/live-common@2.16.1": + version "2.16.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-2.16.1.tgz#f3e1d2a616d5181a71c3982158a5d17d3c562ca1" dependencies: axios "^0.18.0" invariant "^2.2.2"