-
Notifications
You must be signed in to change notification settings - Fork 327
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ (device-core): add new reinstallConfiguration consent use case
- Loading branch information
1 parent
042e1ab
commit 7029023
Showing
14 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
"@ledgerhq/errors": patch | ||
"ledger-live-desktop": patch | ||
"live-mobile": patch | ||
--- | ||
|
||
Add new PINNotSet error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@ledgerhq/device-core": minor | ||
--- | ||
|
||
Add new reinstallConfiguration consent use case |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@ledgerhq/live-cli": minor | ||
--- | ||
|
||
Implement reinstallConfiguration command |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { withDevice } from "@ledgerhq/live-common/hw/deviceAccess"; | ||
import getDeviceInfo from "@ledgerhq/live-common/hw/getDeviceInfo"; | ||
import customLockScreenFetchHash from "@ledgerhq/live-common/hw/customLockScreenFetchHash"; | ||
import listApps from "@ledgerhq/live-common/hw/listApps"; | ||
import { getAppStorageInfo, isCustomLockScreenSupported } from "@ledgerhq/device-core"; | ||
import { reinstallConfiguration } from "@ledgerhq/device-core"; | ||
import { ReinstallConfigArgs } from "@ledgerhq/device-core/commands/entities/ReinstallConfigEntity"; | ||
import { identifyTargetId } from "@ledgerhq/devices"; | ||
import { deviceOpt } from "../../scan"; | ||
import { from, map, switchMap } from "rxjs"; | ||
|
||
export default { | ||
description: | ||
"Consent to allow restoring state of device after a firmware update (apps, language pack, custom lock screen and app data)", | ||
args: [ | ||
deviceOpt, | ||
{ | ||
name: "format", | ||
alias: "f", | ||
type: String, | ||
typeDesc: "raw | json | default", | ||
}, | ||
], | ||
job: ({ device }: { device: string }) => { | ||
return withDevice(device || "")(t => | ||
from(listApps(t)).pipe( | ||
map(apps => apps.filter(app => !!app.name)), | ||
switchMap(async apps => { | ||
const reinstallAppsLength = apps.length; | ||
let storageLength = 0; | ||
for (const app of apps) { | ||
const appStorageInfo = await getAppStorageInfo(t, app.name); | ||
if (appStorageInfo) { | ||
storageLength++; | ||
} | ||
} | ||
const deviceInfo = await getDeviceInfo(t); | ||
if (!deviceInfo.seTargetId) throw new Error("Cannot get device info"); | ||
const deviceModel = identifyTargetId(deviceInfo.seTargetId); | ||
if (!deviceModel) throw new Error("Cannot get device model"); | ||
|
||
const cls = isCustomLockScreenSupported(deviceModel.id) | ||
? await customLockScreenFetchHash(t) | ||
: false; | ||
|
||
const langId = deviceInfo?.languageId ?? 0; | ||
|
||
const args: ReinstallConfigArgs = [ | ||
langId > 0 ? 0x01 : 0x00, | ||
cls ? 0x01 : 0x00, | ||
reinstallAppsLength, | ||
storageLength, | ||
]; | ||
|
||
return args; | ||
}), | ||
switchMap(args => reinstallConfiguration(t, args)), | ||
), | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
libs/device-core/src/commands/entities/ReinstallConfigEntity.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export type ReinstallConfigArgs = [ | ||
// 0x00 = false, 0x01 = true | ||
ReinstallLanguagePack: 0x00 | 0x01, // 1 byte | ||
ReinstallCustomLockScreen: 0x00 | 0x01, // 1 byte | ||
ReinstallAppsNum: number, // 1 byte UINT8 | ||
ReinstallAppDataNum: number, // 1 byte UINT8 | ||
]; | ||
|
||
// TODO: model used when getting the config from the device before a software update | ||
// export type ReinstallConfig = { | ||
// languageId?: LanguageId, | ||
// CustomLockScreen?: CustomLockScreen, | ||
// reinstallApps: AppName[], | ||
// reinstallStorage: AppName[], | ||
// }; |
56 changes: 56 additions & 0 deletions
56
libs/device-core/src/commands/use-cases/consent/reinstallConfiguration.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import Transport, { StatusCodes, TransportStatusError } from "@ledgerhq/hw-transport"; | ||
import { reinstallConfiguration } from "./reinstallConfiguration"; | ||
import { PinNotSet, UserRefusedOnDevice } from "@ledgerhq/errors"; | ||
|
||
describe("reinstallConfiguration", () => { | ||
let transport: Transport; | ||
|
||
beforeEach(() => { | ||
transport = { | ||
send: jest.fn().mockResolvedValue(Buffer.from([])), | ||
getTraceContext: jest.fn().mockResolvedValue(undefined), | ||
} as unknown as Transport; | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe("success cases", () => { | ||
it("should call the send function with correct parameters", async () => { | ||
transport.send = jest.fn().mockResolvedValue(Buffer.from([0x90, 0x00])); | ||
await reinstallConfiguration(transport, [0x00, 0x00, 0x00, 0x00]); | ||
expect(transport.send).toHaveBeenCalledWith( | ||
0xe0, | ||
0x6f, | ||
0x00, | ||
0x00, | ||
Buffer.from([0x00, 0x00, 0x00, 0x00]), | ||
[StatusCodes.OK, StatusCodes.USER_REFUSED_ON_DEVICE, StatusCodes.PIN_NOT_SET], | ||
); | ||
}); | ||
}); | ||
|
||
describe("error cases", () => { | ||
it("should throw UserRefusedOnDevice if the user refused on device", async () => { | ||
transport.send = jest.fn().mockResolvedValue(Buffer.from([0x55, 0x01])); | ||
await expect(reinstallConfiguration(transport, [0x00, 0x00, 0x00, 0x00])).rejects.toThrow( | ||
new UserRefusedOnDevice("User refused on device"), | ||
); | ||
}); | ||
|
||
it("should throw PINNotSet if the PIN is not set", async () => { | ||
transport.send = jest.fn().mockResolvedValue(Buffer.from([0x55, 0x02])); | ||
await expect(reinstallConfiguration(transport, [0x00, 0x00, 0x00, 0x00])).rejects.toThrow( | ||
new PinNotSet("PIN not set"), | ||
); | ||
}); | ||
|
||
it("should throw TransportStatusError if the response status is invalid", async () => { | ||
transport.send = jest.fn().mockResolvedValue(Buffer.from([0x6f, 0x00])); | ||
await expect(reinstallConfiguration(transport, [0x00, 0x00, 0x00, 0x00])).rejects.toThrow( | ||
new TransportStatusError(0x6f00), | ||
); | ||
}); | ||
}); | ||
}); |
75 changes: 75 additions & 0 deletions
75
libs/device-core/src/commands/use-cases/consent/reinstallConfiguration.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import Transport, { StatusCodes, TransportStatusError } from "@ledgerhq/hw-transport"; | ||
import { LocalTracer } from "@ledgerhq/logs"; | ||
import { UserRefusedOnDevice, PinNotSet } from "@ledgerhq/errors"; | ||
import type { APDU } from "../../entities/APDU"; | ||
import type { ReinstallConfigArgs } from "../../entities/ReinstallConfigEntity"; | ||
|
||
/** | ||
* Name in documentation: REINSTALL_CONFIG | ||
* cla: 0xe0 | ||
* ins: 0x6f | ||
* p1: 0x00 | ||
* p2: 0x00 | ||
* data: CHUNK_LEN + CHUNK to configure at runtime | ||
*/ | ||
const REINSTALL_CONFIG = [0xe0, 0x6f, 0x00, 0x00] as const; | ||
|
||
/** | ||
* 0x9000: Success. | ||
* 0xYYYY: already in REINSTALL mode | ||
* 0xZZZZ: if other error (TBD) | ||
*/ | ||
const RESPONSE_STATUS_SET: number[] = [ | ||
StatusCodes.OK, | ||
StatusCodes.USER_REFUSED_ON_DEVICE, | ||
StatusCodes.PIN_NOT_SET, | ||
]; | ||
|
||
/** | ||
* Requests consent from the user to allow reinstalling all the previous | ||
* settings after an OS update. | ||
* | ||
* @param transport - The transport object used to communicate with the device. | ||
* @returns A promise that resolves when the consent is granted. | ||
*/ | ||
export async function reinstallConfiguration( | ||
transport: Transport, | ||
args: ReinstallConfigArgs, | ||
): Promise<void> { | ||
const tracer = new LocalTracer("hw", { | ||
transport: transport.getTraceContext(), | ||
function: "reinstallConfiguration", | ||
}); | ||
tracer.trace("Start"); | ||
|
||
const apdu: Readonly<APDU> = [...REINSTALL_CONFIG, Buffer.from(args)]; | ||
|
||
const response = await transport.send(...apdu, RESPONSE_STATUS_SET); | ||
|
||
return parseResponse(response); | ||
} | ||
|
||
/** | ||
* Parses the response data buffer, check the status code and return the data. | ||
* | ||
* @param data - The response data buffer w/ status code. | ||
* @returns The response data as a buffer w/o status code. | ||
*/ | ||
export function parseResponse(data: Buffer): void { | ||
const tracer = new LocalTracer("hw", { | ||
function: "parseResponse@reinstallConfiguration", | ||
}); | ||
const status = data.readUInt16BE(data.length - 2); | ||
tracer.trace("Result status from 0xe06f0000", { status }); | ||
|
||
switch (status) { | ||
case StatusCodes.OK: | ||
return; | ||
case StatusCodes.USER_REFUSED_ON_DEVICE: | ||
throw new UserRefusedOnDevice("User refused on device"); | ||
case StatusCodes.PIN_NOT_SET: | ||
throw new PinNotSet("PIN not set"); | ||
default: | ||
throw new TransportStatusError(status); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.