Gaëtan Renaudeau
6 years ago
41 changed files with 38 additions and 780 deletions
@ -1,21 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import { createCommand, Command } from 'helpers/ipc' |
|
||||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|
||||
|
|
||||
import getCurrentFirmware from 'helpers/devices/getCurrentFirmware' |
|
||||
import type { FinalFirmware } from 'helpers/types' |
|
||||
|
|
||||
type Input = { |
|
||||
deviceId: string | number, |
|
||||
fullVersion: string, |
|
||||
provider: number, |
|
||||
} |
|
||||
|
|
||||
type Result = FinalFirmware |
|
||||
|
|
||||
const cmd: Command<Input, Result> = createCommand('getCurrentFirmware', data => |
|
||||
fromPromise(getCurrentFirmware(data)), |
|
||||
) |
|
||||
|
|
||||
export default cmd |
|
@ -1,19 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import { createCommand, Command } from 'helpers/ipc' |
|
||||
import { from } from 'rxjs' |
|
||||
|
|
||||
import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' |
|
||||
import getMemInfo from 'helpers/devices/getMemInfo' |
|
||||
|
|
||||
type Input = { |
|
||||
devicePath: string, |
|
||||
} |
|
||||
|
|
||||
type Result = * |
|
||||
|
|
||||
const cmd: Command<Input, Result> = createCommand('getMemInfo', ({ devicePath }) => |
|
||||
withDevice(devicePath)(transport => from(getMemInfo(transport))), |
|
||||
) |
|
||||
|
|
||||
export default cmd |
|
@ -1,19 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import { createCommand, Command } from 'helpers/ipc' |
|
||||
import { from } from 'rxjs' |
|
||||
import { withDevice } from '@ledgerhq/live-common/lib/hw/deviceAccess' |
|
||||
|
|
||||
import isDashboardOpen from '../helpers/devices/isDashboardOpen' |
|
||||
|
|
||||
type Input = { |
|
||||
devicePath: string, |
|
||||
} |
|
||||
|
|
||||
type Result = boolean |
|
||||
|
|
||||
const cmd: Command<Input, Result> = createCommand('isDashboardOpen', ({ devicePath }) => |
|
||||
withDevice(devicePath)(transport => from(isDashboardOpen(transport))), |
|
||||
) |
|
||||
|
|
||||
export default cmd |
|
@ -1,15 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import { createCommand, Command } from 'helpers/ipc' |
|
||||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|
||||
import type { DeviceInfo, ApplicationVersion } from 'helpers/types' |
|
||||
|
|
||||
import listAppVersions from 'helpers/apps/listAppVersions' |
|
||||
|
|
||||
type Result = Array<ApplicationVersion> |
|
||||
|
|
||||
const cmd: Command<DeviceInfo, Result> = createCommand('listAppVersions', deviceInfo => |
|
||||
fromPromise(listAppVersions(deviceInfo)), |
|
||||
) |
|
||||
|
|
||||
export default cmd |
|
@ -1,15 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import { createCommand, Command } from 'helpers/ipc' |
|
||||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|
||||
|
|
||||
import listApps from 'helpers/apps/listApps' |
|
||||
import type { Application } from 'helpers/types' |
|
||||
|
|
||||
type Input = void |
|
||||
|
|
||||
type Result = Array<Application> |
|
||||
|
|
||||
const cmd: Command<Input, Result> = createCommand('listApps', () => fromPromise(listApps())) |
|
||||
|
|
||||
export default cmd |
|
@ -1,17 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import { createCommand, Command } from 'helpers/ipc' |
|
||||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|
||||
|
|
||||
import listCategories from 'helpers/apps/listCategories' |
|
||||
import type { Category } from 'helpers/types' |
|
||||
|
|
||||
type Input = void |
|
||||
|
|
||||
type Result = Array<Category> |
|
||||
|
|
||||
const cmd: Command<Input, Result> = createCommand('listCategories', () => |
|
||||
fromPromise(listCategories()), |
|
||||
) |
|
||||
|
|
||||
export default cmd |
|
@ -1,52 +0,0 @@ |
|||||
// @flow
|
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
|
|
||||
import { createDeviceSocket } from 'helpers/socket' |
|
||||
|
|
||||
import type { ApplicationVersion } from 'helpers/types' |
|
||||
import { WS_INSTALL } from 'helpers/urls' |
|
||||
|
|
||||
import { |
|
||||
ManagerNotEnoughSpaceError, |
|
||||
ManagerDeviceLockedError, |
|
||||
ManagerAppAlreadyInstalledError, |
|
||||
ManagerAppRelyOnBTCError, |
|
||||
} from '@ledgerhq/live-common/lib/errors' |
|
||||
|
|
||||
function remapError(promise) { |
|
||||
return promise.catch((e: Error) => { |
|
||||
switch (true) { |
|
||||
case e.message.endsWith('6982'): |
|
||||
throw new ManagerDeviceLockedError() |
|
||||
case e.message.endsWith('6a84') || e.message.endsWith('6a85'): |
|
||||
throw new ManagerNotEnoughSpaceError() |
|
||||
case e.message.endsWith('6a80') || e.message.endsWith('6a81'): |
|
||||
throw new ManagerAppAlreadyInstalledError() |
|
||||
case e.message.endsWith('6a83'): |
|
||||
throw new ManagerAppRelyOnBTCError() |
|
||||
default: |
|
||||
throw e |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Install an app on the device |
|
||||
*/ |
|
||||
export default async function installApp( |
|
||||
transport: Transport<*>, |
|
||||
targetId: string | number, |
|
||||
{ app }: { app: ApplicationVersion }, |
|
||||
): Promise<void> { |
|
||||
const params = { |
|
||||
targetId, |
|
||||
perso: app.perso, |
|
||||
deleteKey: app.delete_key, |
|
||||
firmware: app.firmware, |
|
||||
firmwareKey: app.firmware_key, |
|
||||
hash: app.hash, |
|
||||
} |
|
||||
|
|
||||
const url = WS_INSTALL(params) |
|
||||
await remapError(createDeviceSocket(transport, url).toPromise()) |
|
||||
} |
|
@ -1,30 +0,0 @@ |
|||||
// @flow
|
|
||||
import network from 'api/network' |
|
||||
import type { DeviceInfo, DeviceVersion, FinalFirmware, ApplicationVersion } from 'helpers/types' |
|
||||
|
|
||||
import { APPLICATIONS_BY_DEVICE } from 'helpers/urls' |
|
||||
import getDeviceVersion from 'helpers/devices/getDeviceVersion' |
|
||||
import getCurrentFirmware from 'helpers/devices/getCurrentFirmware' |
|
||||
|
|
||||
type NetworkResponse = { data: { application_versions: Array<ApplicationVersion> } } |
|
||||
|
|
||||
export default async (deviceInfo: DeviceInfo): Promise<Array<ApplicationVersion>> => { |
|
||||
const deviceData: DeviceVersion = await getDeviceVersion( |
|
||||
deviceInfo.targetId, |
|
||||
deviceInfo.providerId, |
|
||||
) |
|
||||
const firmwareData: FinalFirmware = await getCurrentFirmware({ |
|
||||
deviceId: deviceData.id, |
|
||||
fullVersion: deviceInfo.fullVersion, |
|
||||
provider: deviceInfo.providerId, |
|
||||
}) |
|
||||
const params = { |
|
||||
provider: deviceInfo.providerId, |
|
||||
current_se_firmware_final_version: firmwareData.id, |
|
||||
device_version: deviceData.id, |
|
||||
} |
|
||||
const { |
|
||||
data: { application_versions }, |
|
||||
}: NetworkResponse = await network({ method: 'POST', url: APPLICATIONS_BY_DEVICE, data: params }) |
|
||||
return application_versions.length > 0 ? application_versions : [] |
|
||||
} |
|
@ -1,10 +0,0 @@ |
|||||
// @flow
|
|
||||
import network from 'api/network' |
|
||||
|
|
||||
import { GET_APPLICATIONS } from 'helpers/urls' |
|
||||
import type { Application } from 'helpers/types' |
|
||||
|
|
||||
export default async (): Promise<Array<Application>> => { |
|
||||
const { data } = await network({ method: 'GET', url: GET_APPLICATIONS }) |
|
||||
return data.length > 0 ? data : [] |
|
||||
} |
|
@ -1,10 +0,0 @@ |
|||||
// @flow
|
|
||||
import network from 'api/network' |
|
||||
|
|
||||
import { GET_CATEGORIES } from 'helpers/urls' |
|
||||
import type { Category } from 'helpers/types' |
|
||||
|
|
||||
export default async (): Promise<Array<Category>> => { |
|
||||
const { data }: { data: Array<Category> } = await network({ method: 'GET', url: GET_CATEGORIES }) |
|
||||
return data.length > 0 ? data : [] |
|
||||
} |
|
@ -1,41 +0,0 @@ |
|||||
// @flow
|
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
|
|
||||
import { createDeviceSocket } from 'helpers/socket' |
|
||||
|
|
||||
import type { ApplicationVersion } from 'helpers/types' |
|
||||
import { ManagerDeviceLockedError, ManagerUninstallBTCDep } from '@ledgerhq/live-common/lib/errors' |
|
||||
import { WS_INSTALL } from 'helpers/urls' |
|
||||
|
|
||||
function remapError(promise) { |
|
||||
return promise.catch((e: Error) => { |
|
||||
switch (true) { |
|
||||
case e.message.endsWith('6982'): |
|
||||
throw new ManagerDeviceLockedError() |
|
||||
case e.message.endsWith('6a83'): |
|
||||
throw new ManagerUninstallBTCDep() |
|
||||
default: |
|
||||
throw e |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Install an app on the device |
|
||||
*/ |
|
||||
export default async function uninstallApp( |
|
||||
transport: Transport<*>, |
|
||||
targetId: string | number, |
|
||||
{ app }: { app: ApplicationVersion }, |
|
||||
): Promise<void> { |
|
||||
const params = { |
|
||||
targetId, |
|
||||
perso: app.perso, |
|
||||
deleteKey: app.delete_key, |
|
||||
firmware: app.delete, |
|
||||
firmwareKey: app.delete_key, |
|
||||
hash: app.hash, |
|
||||
} |
|
||||
const url = WS_INSTALL(params) |
|
||||
await remapError(createDeviceSocket(transport, url).toPromise()) |
|
||||
} |
|
@ -1,9 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
|
|
||||
export default async (transport: Transport<*>) => { |
|
||||
const r = await transport.send(0xe0, 0xc4, 0, 0) |
|
||||
const version = `${r[2]}.${r[3]}.${r[4]}` |
|
||||
return { version } |
|
||||
} |
|
@ -1,10 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import Eth from '@ledgerhq/hw-app-eth' |
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
|
|
||||
export default async (transport: Transport<*>) => { |
|
||||
const eth = new Eth(transport) |
|
||||
const { version } = await eth.getAppConfiguration() |
|
||||
return { version } |
|
||||
} |
|
@ -1,29 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' |
|
||||
import invariant from 'invariant' |
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
import bitcoin from './btc' |
|
||||
import ethereum from './ethereum' |
|
||||
import ripple from './ripple' |
|
||||
|
|
||||
type Resolver = ( |
|
||||
transport: Transport<*>, |
|
||||
currency: CryptoCurrency, |
|
||||
) => Promise<{ |
|
||||
version?: string, |
|
||||
}> |
|
||||
|
|
||||
const perFamily: { [_: string]: * } = { |
|
||||
bitcoin, |
|
||||
ethereum, |
|
||||
ripple, |
|
||||
} |
|
||||
|
|
||||
const proxy: Resolver = (transport, currency) => { |
|
||||
const getAddress = perFamily[currency.family] |
|
||||
invariant(getAddress, `getAddress not implemented for ${currency.id}`) |
|
||||
return getAddress(transport) |
|
||||
} |
|
||||
|
|
||||
export default proxy |
|
@ -1,10 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import Xrp from '@ledgerhq/hw-app-xrp' |
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
|
|
||||
export default async (transport: Transport<*>) => { |
|
||||
const xrp = new Xrp(transport) |
|
||||
const { version } = await xrp.getAppConfiguration() |
|
||||
return { version } |
|
||||
} |
|
@ -1,21 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
|
|
||||
const getBitcoinLikeInfo = ( |
|
||||
transport: Transport<any>, |
|
||||
): 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 |
|
@ -1,24 +0,0 @@ |
|||||
// @flow
|
|
||||
import network from 'api/network' |
|
||||
|
|
||||
import { GET_CURRENT_FIRMWARE } from 'helpers/urls' |
|
||||
import type { FinalFirmware } from 'helpers/types' |
|
||||
|
|
||||
type Input = { |
|
||||
fullVersion: string, |
|
||||
deviceId: string | number, |
|
||||
provider: number, |
|
||||
} |
|
||||
|
|
||||
export default async (input: Input): Promise<FinalFirmware> => { |
|
||||
const { data }: { data: FinalFirmware } = await network({ |
|
||||
method: 'POST', |
|
||||
url: GET_CURRENT_FIRMWARE, |
|
||||
data: { |
|
||||
device_version: input.deviceId, |
|
||||
version_name: input.fullVersion, |
|
||||
provider: input.provider, |
|
||||
}, |
|
||||
}) |
|
||||
return data |
|
||||
} |
|
@ -1,5 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import getDeviceInfo from '@ledgerhq/live-common/lib/hw/getDeviceInfo' |
|
||||
|
|
||||
export default getDeviceInfo |
|
@ -1,17 +0,0 @@ |
|||||
// @flow
|
|
||||
import { GET_DEVICE_VERSION } from 'helpers/urls' |
|
||||
import network from 'api/network' |
|
||||
|
|
||||
import type { DeviceVersion } from 'helpers/types' |
|
||||
|
|
||||
export default async (targetId: string | number, provider: number): Promise<DeviceVersion> => { |
|
||||
const { data }: { data: DeviceVersion } = await network({ |
|
||||
method: 'POST', |
|
||||
url: GET_DEVICE_VERSION, |
|
||||
data: { |
|
||||
provider, |
|
||||
target_id: targetId, |
|
||||
}, |
|
||||
}) |
|
||||
return data |
|
||||
} |
|
@ -1,32 +0,0 @@ |
|||||
// @flow
|
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
import { SKIP_GENUINE } from 'config/constants' |
|
||||
import { WS_GENUINE } from 'helpers/urls' |
|
||||
import type { DeviceInfo, FinalFirmware, DeviceVersion } from 'helpers/types' |
|
||||
|
|
||||
import { createDeviceSocket } from 'helpers/socket' |
|
||||
import getCurrentFirmware from './getCurrentFirmware' |
|
||||
import getDeviceVersion from './getDeviceVersion' |
|
||||
|
|
||||
export default async (transport: Transport<*>, deviceInfo: DeviceInfo): Promise<string> => { |
|
||||
const deviceVersion: DeviceVersion = await getDeviceVersion( |
|
||||
deviceInfo.targetId, |
|
||||
deviceInfo.providerId, |
|
||||
) |
|
||||
|
|
||||
const firmware: FinalFirmware = await getCurrentFirmware({ |
|
||||
deviceId: deviceVersion.id, |
|
||||
fullVersion: deviceInfo.fullVersion, |
|
||||
provider: deviceInfo.providerId, |
|
||||
}) |
|
||||
|
|
||||
const params = { |
|
||||
targetId: deviceInfo.targetId, |
|
||||
perso: firmware.perso, |
|
||||
} |
|
||||
|
|
||||
const url = WS_GENUINE(params) |
|
||||
return SKIP_GENUINE |
|
||||
? new Promise(resolve => setTimeout(() => resolve('0000'), 1000)) |
|
||||
: createDeviceSocket(transport, url).toPromise() |
|
||||
} |
|
@ -1,10 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
|
|
||||
import getFirmwareInfo from 'helpers/firmware/getFirmwareInfo' |
|
||||
|
|
||||
export default async function getMemInfos(transport: Transport<*>): Promise<Object> { |
|
||||
const { targetId } = await getFirmwareInfo(transport) // eslint-disable-line
|
|
||||
return new Promise(resolve => setTimeout(() => resolve({}), 1000)) |
|
||||
} |
|
@ -1,23 +0,0 @@ |
|||||
// @flow
|
|
||||
import network from 'api/network' |
|
||||
|
|
||||
import { GET_CURRENT_OSU } from 'helpers/urls' |
|
||||
|
|
||||
type Input = { |
|
||||
version: string, |
|
||||
deviceId: string | number, |
|
||||
provider: number, |
|
||||
} |
|
||||
|
|
||||
export default async (input: Input): Promise<*> => { |
|
||||
const { data } = await network({ |
|
||||
method: 'POST', |
|
||||
url: GET_CURRENT_OSU, |
|
||||
data: { |
|
||||
device_version: input.deviceId, |
|
||||
version_name: `${input.version}-osu`, |
|
||||
provider: input.provider, |
|
||||
}, |
|
||||
}) |
|
||||
return data |
|
||||
} |
|
@ -1,16 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
|
|
||||
import getFirmwareInfo from 'helpers/firmware/getFirmwareInfo' |
|
||||
|
|
||||
type Result = boolean |
|
||||
|
|
||||
export default async (transport: Transport<*>): Promise<Result> => { |
|
||||
const { targetId, seVersion } = await getFirmwareInfo(transport) |
|
||||
if (targetId && seVersion) { |
|
||||
return true |
|
||||
} |
|
||||
|
|
||||
return false |
|
||||
} |
|
@ -1,8 +0,0 @@ |
|||||
// @flow
|
|
||||
import network from 'api/network' |
|
||||
import { GET_FINAL_FIRMWARE } from 'helpers/urls' |
|
||||
|
|
||||
export default async (id: number) => { |
|
||||
const { data } = await network({ method: 'GET', url: `${GET_FINAL_FIRMWARE}/${id}` }) |
|
||||
return data |
|
||||
} |
|
@ -1,51 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
import type { FirmwareInfo } from 'helpers/types' |
|
||||
|
|
||||
const APDUS = { |
|
||||
GET_FIRMWARE: [0xe0, 0x01, 0x00, 0x00], |
|
||||
// we dont have common call that works inside app & dashboard
|
|
||||
// TODO: this should disappear.
|
|
||||
GET_FIRMWARE_FALLBACK: [0xe0, 0xc4, 0x00, 0x00], |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Retrieve targetId and firmware version from device |
|
||||
*/ |
|
||||
export default async function getFirmwareInfo(transport: Transport<*>): Promise<FirmwareInfo> { |
|
||||
const res = await transport.send(...APDUS.GET_FIRMWARE) |
|
||||
const byteArray = [...res] |
|
||||
const data = byteArray.slice(0, byteArray.length - 2) |
|
||||
const targetIdStr = Buffer.from(data.slice(0, 4)) |
|
||||
const targetId = targetIdStr.readUIntBE(0, 4) |
|
||||
const seVersionLength = data[4] |
|
||||
const seVersion = Buffer.from(data.slice(5, 5 + seVersionLength)).toString() |
|
||||
const flagsLength = data[5 + seVersionLength] |
|
||||
const flags = Buffer.from( |
|
||||
data.slice(5 + seVersionLength + 1, 5 + seVersionLength + 1 + flagsLength), |
|
||||
).toString() |
|
||||
|
|
||||
const mcuVersionLength = data[5 + seVersionLength + 1 + flagsLength] |
|
||||
let mcuVersion = Buffer.from( |
|
||||
data.slice( |
|
||||
7 + seVersionLength + flagsLength, |
|
||||
7 + seVersionLength + flagsLength + mcuVersionLength, |
|
||||
), |
|
||||
) |
|
||||
if (mcuVersion[mcuVersion.length - 1] === 0) { |
|
||||
mcuVersion = mcuVersion.slice(0, mcuVersion.length - 1) |
|
||||
} |
|
||||
mcuVersion = mcuVersion.toString() |
|
||||
|
|
||||
if (!seVersionLength) { |
|
||||
return { |
|
||||
targetId, |
|
||||
seVersion: '0.0.0', |
|
||||
flags: '', |
|
||||
mcuVersion: '', |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return { targetId, seVersion, flags, mcuVersion } |
|
||||
} |
|
@ -1,13 +0,0 @@ |
|||||
// @flow
|
|
||||
import network from 'api/network' |
|
||||
|
|
||||
import { GET_MCUS } from 'helpers/urls' |
|
||||
|
|
||||
export default async (): Promise<*> => { |
|
||||
const { data } = await network({ |
|
||||
method: 'GET', |
|
||||
url: GET_MCUS, |
|
||||
}) |
|
||||
|
|
||||
return data |
|
||||
} |
|
@ -1,25 +0,0 @@ |
|||||
// @flow
|
|
||||
import network from 'api/network' |
|
||||
|
|
||||
import { GET_NEXT_MCU } from 'helpers/urls' |
|
||||
import type { OsuFirmware } from 'helpers/types' |
|
||||
import { LatestMCUInstalledError } from '@ledgerhq/live-common/lib/errors' |
|
||||
|
|
||||
type NetworkResponse = { data: OsuFirmware | 'default' } |
|
||||
|
|
||||
export default async (bootloaderVersion: string): Promise<*> => { |
|
||||
const { data }: NetworkResponse = await network({ |
|
||||
method: 'POST', |
|
||||
url: GET_NEXT_MCU, |
|
||||
data: { |
|
||||
bootloader_version: bootloaderVersion, |
|
||||
}, |
|
||||
}) |
|
||||
|
|
||||
// FIXME: nextVersion will not be able to "default" when
|
|
||||
// Error handling is standardize on the API side
|
|
||||
if (data === 'default' || !data.name) { |
|
||||
throw new LatestMCUInstalledError('there is no next mcu version to install') |
|
||||
} |
|
||||
return data |
|
||||
} |
|
@ -1,132 +0,0 @@ |
|||||
// @flow
|
|
||||
|
|
||||
import invariant from 'invariant' |
|
||||
import logger from 'logger' |
|
||||
import Websocket from 'ws' |
|
||||
import type Transport from '@ledgerhq/hw-transport' |
|
||||
import { Observable } from 'rxjs' |
|
||||
import { |
|
||||
WebsocketConnectionError, |
|
||||
WebsocketConnectionFailed, |
|
||||
DeviceSocketFail, |
|
||||
DeviceSocketNoBulkStatus, |
|
||||
DeviceSocketNoHandler, |
|
||||
} from '@ledgerhq/live-common/lib/errors' |
|
||||
|
|
||||
/** |
|
||||
* use Ledger WebSocket API to exchange data with the device |
|
||||
* Returns an Observable of the final result |
|
||||
*/ |
|
||||
export const createDeviceSocket = (transport: Transport<*>, url: string) => |
|
||||
Observable.create(o => { |
|
||||
let ws |
|
||||
let lastMessage: ?string |
|
||||
|
|
||||
try { |
|
||||
ws = new Websocket(url) |
|
||||
} catch (err) { |
|
||||
o.error(new WebsocketConnectionFailed(err.message, { url })) |
|
||||
return () => {} |
|
||||
} |
|
||||
invariant(ws, 'websocket is available') |
|
||||
|
|
||||
ws.on('open', () => { |
|
||||
logger.websocket('OPENED', { url }) |
|
||||
}) |
|
||||
|
|
||||
ws.on('error', e => { |
|
||||
logger.websocket('ERROR', { message: e.message, stack: e.stack }) |
|
||||
o.error(new WebsocketConnectionError(e.message, { url })) |
|
||||
}) |
|
||||
|
|
||||
ws.on('close', () => { |
|
||||
logger.websocket('CLOSE') |
|
||||
o.next(lastMessage || '') |
|
||||
o.complete() |
|
||||
}) |
|
||||
|
|
||||
const send = (nonce, response, data) => { |
|
||||
const msg = { |
|
||||
nonce, |
|
||||
response, |
|
||||
data, |
|
||||
} |
|
||||
logger.websocket('SEND', msg) |
|
||||
const strMsg = JSON.stringify(msg) |
|
||||
ws.send(strMsg) |
|
||||
} |
|
||||
|
|
||||
const handlers = { |
|
||||
exchange: async input => { |
|
||||
const { data, nonce } = input |
|
||||
const r: Buffer = await transport.exchange(Buffer.from(data, 'hex')) |
|
||||
const status = r.slice(r.length - 2) |
|
||||
const buffer = r.slice(0, r.length - 2) |
|
||||
const strStatus = status.toString('hex') |
|
||||
send(nonce, strStatus === '9000' ? 'success' : 'error', buffer.toString('hex')) |
|
||||
}, |
|
||||
|
|
||||
bulk: async input => { |
|
||||
const { data, nonce } = input |
|
||||
|
|
||||
// Execute all apdus and collect last status
|
|
||||
let lastStatus = null |
|
||||
for (const apdu of data) { |
|
||||
const r: Buffer = await transport.exchange(Buffer.from(apdu, 'hex')) |
|
||||
lastStatus = r.slice(r.length - 2) |
|
||||
|
|
||||
if (lastStatus.toString('hex') !== '9000') break |
|
||||
} |
|
||||
|
|
||||
if (!lastStatus) { |
|
||||
throw new DeviceSocketNoBulkStatus() |
|
||||
} |
|
||||
|
|
||||
const strStatus = lastStatus.toString('hex') |
|
||||
|
|
||||
send( |
|
||||
nonce, |
|
||||
strStatus === '9000' ? 'success' : 'error', |
|
||||
strStatus === '9000' ? '' : strStatus, |
|
||||
) |
|
||||
}, |
|
||||
|
|
||||
success: msg => { |
|
||||
lastMessage = msg.data || msg.result |
|
||||
ws.close() |
|
||||
}, |
|
||||
|
|
||||
error: msg => { |
|
||||
logger.websocket('ERROR', { data: msg.data }) |
|
||||
throw new DeviceSocketFail(msg.data, { url }) |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
const stackMessage = async rawMsg => { |
|
||||
try { |
|
||||
const msg = JSON.parse(rawMsg) |
|
||||
if (!(msg.query in handlers)) { |
|
||||
throw new DeviceSocketNoHandler(`Cannot handle msg of type ${msg.query}`, { |
|
||||
query: msg.query, |
|
||||
url, |
|
||||
}) |
|
||||
} |
|
||||
logger.websocket('RECEIVE', msg) |
|
||||
await handlers[msg.query](msg) |
|
||||
} catch (err) { |
|
||||
logger.websocket('ERROR', { message: err.message, stack: err.stack }) |
|
||||
o.error(err) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
ws.on('message', async rawMsg => { |
|
||||
stackMessage(rawMsg) |
|
||||
}) |
|
||||
|
|
||||
return () => { |
|
||||
if (ws.readyState === 1) { |
|
||||
lastMessage = null |
|
||||
ws.close() |
|
||||
} |
|
||||
} |
|
||||
}) |
|
@ -1,37 +0,0 @@ |
|||||
// @flow
|
|
||||
import qs from 'qs' |
|
||||
|
|
||||
import { MANAGER_API_BASE, BASE_SOCKET_URL } from 'config/constants' |
|
||||
import type { LedgerScriptParams } from 'helpers/types' |
|
||||
|
|
||||
const urlBuilder = (base: string) => (endpoint: string): string => `${base}/${endpoint}` |
|
||||
|
|
||||
const managerUrlbuilder = urlBuilder(MANAGER_API_BASE) |
|
||||
|
|
||||
const wsURLBuilder = (endpoint: string) => (params?: Object) => |
|
||||
`${BASE_SOCKET_URL}/${endpoint}${params ? `?${qs.stringify(params)}` : ''}` |
|
||||
|
|
||||
// const wsURLBuilderProxy = (endpoint: string) => (params?: Object) =>
|
|
||||
// `ws://manager.ledger.fr:3501/${endpoint}${params ? `?${qs.stringify(params)}` : ''}`
|
|
||||
|
|
||||
// FIXME we shouldn't do this here. we should just collocate these where it's used.
|
|
||||
|
|
||||
export const GET_FINAL_FIRMWARE: string = managerUrlbuilder('firmware_final_versions') |
|
||||
export const GET_DEVICE_VERSION: string = managerUrlbuilder('get_device_version') |
|
||||
export const APPLICATIONS_BY_DEVICE: string = managerUrlbuilder('get_apps') |
|
||||
export const GET_CURRENT_FIRMWARE: string = managerUrlbuilder('get_firmware_version') |
|
||||
export const GET_CURRENT_OSU: string = managerUrlbuilder('get_osu_version') |
|
||||
export const GET_LATEST_FIRMWARE: string = managerUrlbuilder('get_latest_firmware') |
|
||||
export const GET_NEXT_MCU: string = managerUrlbuilder('mcu_versions_bootloader') |
|
||||
export const GET_MCUS: string = managerUrlbuilder('mcu_versions') |
|
||||
export const GET_CATEGORIES: string = managerUrlbuilder('categories') |
|
||||
export const GET_APPLICATIONS: string = managerUrlbuilder('applications') |
|
||||
|
|
||||
export const WS_INSTALL: (arg: LedgerScriptParams) => string = wsURLBuilder('install') |
|
||||
export const WS_GENUINE: (arg: { |
|
||||
targetId: string | number, |
|
||||
perso: string, |
|
||||
}) => string = wsURLBuilder('genuine') |
|
||||
export const WS_MCU: (arg: { targetId: string | number, version: string }) => string = wsURLBuilder( |
|
||||
'mcu', |
|
||||
) |
|
Loading…
Reference in new issue