@onekeyfe/hd-core 1.2.0-alpha.45 → 1.2.0-alpha.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/deviceUploadNft.test.ts +21 -0
- package/__tests__/open-wallet-session.test.ts +33 -1
- package/__tests__/protocol-v2-resources.test.ts +3 -38
- package/__tests__/protocol-v2.test.ts +7 -109
- package/__tests__/uiPromiseRegistry.test.ts +86 -0
- package/dist/api/FirmwareUpdateV4.d.ts +0 -2
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/uiPromiseRegistry.d.ts +4 -0
- package/dist/core/uiPromiseRegistry.d.ts.map +1 -0
- package/dist/data-manager/DataManager.d.ts +0 -1
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/events/ui-promise.d.ts +4 -2
- package/dist/events/ui-promise.d.ts.map +1 -1
- package/dist/events/ui-request.d.ts +3 -0
- package/dist/events/ui-request.d.ts.map +1 -1
- package/dist/events/ui-response.d.ts +10 -2
- package/dist/events/ui-response.d.ts.map +1 -1
- package/dist/index.d.ts +52 -58
- package/dist/index.js +52 -109
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/walletSession.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +1 -2
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/settings.d.ts +0 -10
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +2 -2
- package/dist/utils/pro2Nft.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +6 -76
- package/src/core/index.ts +20 -4
- package/src/core/uiPromiseRegistry.ts +41 -0
- package/src/data-manager/DataManager.ts +0 -4
- package/src/events/ui-promise.ts +4 -2
- package/src/events/ui-request.ts +3 -0
- package/src/events/ui-response.ts +12 -2
- package/src/protocols/protocol-v2/resources.ts +1 -33
- package/src/protocols/protocol-v2/walletSession.ts +5 -1
- package/src/types/api/firmwareUpdate.ts +0 -3
- package/src/types/settings.ts +0 -16
- package/src/utils/pro2Nft.ts +2 -2
package/dist/utils/pro2Nft.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ export declare const PRO2_NFT_IMAGE_HEIGHT = 540;
|
|
|
3
3
|
export declare const PRO2_NFT_THUMBNAIL_WIDTH = 263;
|
|
4
4
|
export declare const PRO2_NFT_THUMBNAIL_HEIGHT = 263;
|
|
5
5
|
export declare const PRO2_NFT_DIRECTORY = "vol1:/nft";
|
|
6
|
-
export declare const PRO2_NFT_DEFAULT_CHUNK_SIZE =
|
|
7
|
-
export declare const PRO2_NFT_DEFAULT_PACE_MS =
|
|
6
|
+
export declare const PRO2_NFT_DEFAULT_CHUNK_SIZE = 2048;
|
|
7
|
+
export declare const PRO2_NFT_DEFAULT_PACE_MS = 0;
|
|
8
8
|
export declare const PRO2_NFT_DEFAULT_TIMEOUT_MS = 15000;
|
|
9
9
|
export declare const PRO2_NFT_MIN_CHUNK_SIZE = 64;
|
|
10
10
|
export declare const PRO2_NFT_MAX_CHUNK_SIZE = 2048;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pro2Nft.d.ts","sourceRoot":"","sources":["../../src/utils/pro2Nft.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAC9C,eAAO,MAAM,2BAA2B,
|
|
1
|
+
{"version":3,"file":"pro2Nft.d.ts","sourceRoot":"","sources":["../../src/utils/pro2Nft.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAC9C,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAClD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,GAAG,WAAW,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,UAAU,CAAC;IACtB,QAAQ,EAAE,UAAU,CAAC;CACtB,CAAC;AAiCF,wBAAgB,2BAA2B,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiB5E;AAsBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,YAAY,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,aAAa,CAkChB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
3
|
+
"version": "1.2.0-alpha.46",
|
|
4
4
|
"description": "Core processes and APIs for communicating with OneKey hardware devices.",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@onekeyfe/hd-shared": "1.2.0-alpha.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.2.0-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.2.0-alpha.46",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.2.0-alpha.46",
|
|
30
30
|
"axios": "1.15.2",
|
|
31
31
|
"bignumber.js": "^9.0.2",
|
|
32
32
|
"bytebuffer": "^5.0.1",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"@types/w3c-web-usb": "^1.0.10",
|
|
45
45
|
"@types/web-bluetooth": "^0.0.21"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "23bc93ba87e4f8557380c636b308aea06f569ae2"
|
|
48
48
|
}
|
|
@@ -45,7 +45,6 @@ import type { TypedResponseMessage } from '../device/DeviceCommands';
|
|
|
45
45
|
import type {
|
|
46
46
|
Features,
|
|
47
47
|
IFirmwareReleaseInfo,
|
|
48
|
-
IProtocolV2BootResources,
|
|
49
48
|
IProtocolV2FirmwareComponent,
|
|
50
49
|
IProtocolV2Resource,
|
|
51
50
|
IVersionArray,
|
|
@@ -75,7 +74,6 @@ const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
|
75
74
|
const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
|
|
76
75
|
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
|
|
77
76
|
const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
|
|
78
|
-
const PROTOCOL_V2_BOOT_RESOURCES_FILE_NAME = 'boot_resources.crate.okpkg';
|
|
79
77
|
|
|
80
78
|
const getProtocolV2DeviceTransferProgress = (
|
|
81
79
|
bytesBeforeChunk: number,
|
|
@@ -110,7 +108,7 @@ type ProtocolV2FirmwareUpdateStartResponse = TypedResponseMessage<'Success'>;
|
|
|
110
108
|
|
|
111
109
|
type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId: number };
|
|
112
110
|
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
113
|
-
kind: ProtocolV2RemoteComponentTarget['kind']
|
|
111
|
+
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
114
112
|
};
|
|
115
113
|
type ProtocolV2InstallTarget = ProtocolV2InstallItem & {
|
|
116
114
|
path: string;
|
|
@@ -371,11 +369,10 @@ export const assertProtocolV2ReconnectIdentity = (
|
|
|
371
369
|
expectedSerialNumber?: string,
|
|
372
370
|
actualSerialNumber?: string
|
|
373
371
|
) => {
|
|
372
|
+
// Some Pro2 loader builds omit serial_no. Reconnect still uses the same BLE
|
|
373
|
+
// descriptor or requires a uniquely enumerated USB device before this check.
|
|
374
374
|
if (!expectedSerialNumber || !actualSerialNumber) {
|
|
375
|
-
|
|
376
|
-
HardwareErrorCode.DeviceNotFound,
|
|
377
|
-
'Protocol V2 reconnect physical identity is unavailable'
|
|
378
|
-
);
|
|
375
|
+
return;
|
|
379
376
|
}
|
|
380
377
|
if (actualSerialNumber !== expectedSerialNumber) {
|
|
381
378
|
throw ERRORS.TypedError(
|
|
@@ -426,7 +423,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
426
423
|
{ name: 'chunkSize', type: 'number' },
|
|
427
424
|
{ name: 'forcedUpdateRes', type: 'boolean' },
|
|
428
425
|
{ name: 'bootloaderBinary', type: 'buffer' },
|
|
429
|
-
{ name: 'bootResourcesBinary', type: 'buffer' },
|
|
430
426
|
{ name: 'romloaderBinary', type: 'buffer' },
|
|
431
427
|
{ name: 'applicationP1Binary', type: 'buffer' },
|
|
432
428
|
{ name: 'applicationP2Binary', type: 'buffer' },
|
|
@@ -445,7 +441,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
445
441
|
chunkSize: payload.chunkSize,
|
|
446
442
|
forcedUpdateRes: payload.forcedUpdateRes,
|
|
447
443
|
bootloaderBinary: payload.bootloaderBinary,
|
|
448
|
-
bootResourcesBinary: payload.bootResourcesBinary,
|
|
449
444
|
romloaderBinary: payload.romloaderBinary,
|
|
450
445
|
applicationP1Binary: payload.applicationP1Binary,
|
|
451
446
|
applicationP2Binary: payload.applicationP2Binary,
|
|
@@ -494,7 +489,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
494
489
|
|
|
495
490
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
496
491
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
497
|
-
let bootResourcesInstallItem: ProtocolV2InstallItem | undefined;
|
|
498
492
|
let installItems: ProtocolV2InstallItem[] | undefined;
|
|
499
493
|
let resourceBundles: ProtocolV2ResourceBundleBinary[] | undefined;
|
|
500
494
|
try {
|
|
@@ -505,10 +499,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
505
499
|
const needsRemoteResources =
|
|
506
500
|
!this.params.resourceBundleFiles?.length &&
|
|
507
501
|
!!this.params.targetsToUpdate?.includes('resource');
|
|
508
|
-
|
|
509
|
-
!this.params.bootResourcesBinary &&
|
|
510
|
-
!!this.params.targetsToUpdate?.includes('boot_resources');
|
|
511
|
-
if (needsRemoteFirmware || needsRemoteResources || needsRemoteBootResources) {
|
|
502
|
+
if (needsRemoteFirmware || needsRemoteResources) {
|
|
512
503
|
// Remote updates must use a freshly fetched config before any reboot or file write.
|
|
513
504
|
await DataManager.forceReloadData();
|
|
514
505
|
}
|
|
@@ -521,25 +512,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
521
512
|
fwBinaryMap = remoteBinaries.fwBinaryMap;
|
|
522
513
|
installItems = remoteBinaries.installItems;
|
|
523
514
|
}
|
|
524
|
-
bootResourcesInstallItem = await this.prepareProtocolV2BootResources();
|
|
525
|
-
if (bootResourcesInstallItem) {
|
|
526
|
-
installItems = [
|
|
527
|
-
bootResourcesInstallItem,
|
|
528
|
-
...(installItems ?? this.buildProtocolV2InstallItems({ bootloaderBinary, fwBinaryMap })),
|
|
529
|
-
];
|
|
530
|
-
}
|
|
531
515
|
resourceBundles = await this.prepareProtocolV2ResourceBundles(resourceRecoveryMode);
|
|
532
516
|
this.postTipMessage(FirmwareUpdateTipMessage.FinishDownloadFirmware);
|
|
533
517
|
} catch (err) {
|
|
534
518
|
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
535
519
|
}
|
|
536
520
|
|
|
537
|
-
if (
|
|
538
|
-
!bootloaderBinary &&
|
|
539
|
-
fwBinaryMap.length === 0 &&
|
|
540
|
-
!installItems?.length &&
|
|
541
|
-
!resourceBundles?.length
|
|
542
|
-
) {
|
|
521
|
+
if (!bootloaderBinary && fwBinaryMap.length === 0 && !resourceBundles?.length) {
|
|
543
522
|
if (resourceBundles !== undefined) {
|
|
544
523
|
this.postTipMessage(FirmwareUpdateTipMessage.FirmwareUpdateCompleted);
|
|
545
524
|
return this.getProtocolV2VersionResult(deviceFeatures);
|
|
@@ -619,7 +598,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
619
598
|
return (
|
|
620
599
|
!!this.params.resourceBundleFiles?.length ||
|
|
621
600
|
!!this.params.bootloaderBinary ||
|
|
622
|
-
!!this.params.bootResourcesBinary ||
|
|
623
601
|
fwBinaryMap.length > 0
|
|
624
602
|
);
|
|
625
603
|
}
|
|
@@ -769,54 +747,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
769
747
|
};
|
|
770
748
|
}
|
|
771
749
|
|
|
772
|
-
private validateProtocolV2BootResourcesBinary(
|
|
773
|
-
binary: ArrayBuffer,
|
|
774
|
-
resource?: IProtocolV2BootResources
|
|
775
|
-
) {
|
|
776
|
-
if (resource && !isProtocolV2ResourceFileValid(binary, resource)) {
|
|
777
|
-
throw new Error('Pro2 boot resources file verification failed');
|
|
778
|
-
}
|
|
779
|
-
const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(binary));
|
|
780
|
-
if (!header || header.type !== 'CRAT') {
|
|
781
|
-
throw new Error('Invalid Pro2 boot resources CRATE header');
|
|
782
|
-
}
|
|
783
|
-
if (resource) {
|
|
784
|
-
const expectedPayloadHash = normalizeProtocolV2Hex(resource.payloadHash);
|
|
785
|
-
const expectedHeaderHash = normalizeProtocolV2Hex(resource.headerHash);
|
|
786
|
-
if (header.payloadHash !== expectedPayloadHash) {
|
|
787
|
-
throw new Error('Pro2 boot resources payload hash mismatch');
|
|
788
|
-
}
|
|
789
|
-
if (header.headerHash !== expectedHeaderHash) {
|
|
790
|
-
throw new Error('Pro2 boot resources header hash mismatch');
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
private async prepareProtocolV2BootResources(): Promise<ProtocolV2InstallItem | undefined> {
|
|
796
|
-
let binary = this.params.bootResourcesBinary;
|
|
797
|
-
let resource: IProtocolV2BootResources | undefined;
|
|
798
|
-
|
|
799
|
-
if (!binary) {
|
|
800
|
-
if (!this.params.targetsToUpdate?.includes('boot_resources')) {
|
|
801
|
-
return undefined;
|
|
802
|
-
}
|
|
803
|
-
resource = DataManager.getProtocolV2BootResources();
|
|
804
|
-
if (!resource) {
|
|
805
|
-
throw new Error('Missing Pro2 boot resources configuration');
|
|
806
|
-
}
|
|
807
|
-
Log.log('[FirmwareUpdateV4] downloading Pro2 boot resources CRATE');
|
|
808
|
-
({ binary } = await getSysResourceBinary(resource.url));
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
this.validateProtocolV2BootResourcesBinary(binary, resource);
|
|
812
|
-
return {
|
|
813
|
-
fileName: PROTOCOL_V2_BOOT_RESOURCES_FILE_NAME,
|
|
814
|
-
binary,
|
|
815
|
-
targetId: ProtocolV2FirmwareTargetType.FW_MGMT_TARGET_CRATE,
|
|
816
|
-
kind: 'boot_resources',
|
|
817
|
-
};
|
|
818
|
-
}
|
|
819
|
-
|
|
820
750
|
private async prepareProtocolV2ResourceBundles(
|
|
821
751
|
recoveryMode: boolean
|
|
822
752
|
): Promise<ProtocolV2ResourceBundleBinary[] | undefined> {
|
package/src/core/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
createRequestContext,
|
|
27
27
|
createSdkTracingContext,
|
|
28
28
|
formatRequestContext,
|
|
29
|
+
generateInstanceId,
|
|
29
30
|
getActiveRequestsByDeviceInstance,
|
|
30
31
|
updateRequestContext,
|
|
31
32
|
} from '../utils/tracing';
|
|
@@ -53,6 +54,7 @@ import {
|
|
|
53
54
|
import TransportManager from '../data-manager/TransportManager';
|
|
54
55
|
import DeviceConnector from '../device/DeviceConnector';
|
|
55
56
|
import RequestQueue from './RequestQueue';
|
|
57
|
+
import { findUiPromiseForResponse } from './uiPromiseRegistry';
|
|
56
58
|
import { registerHardwareUiEventListeners } from './deviceEventRegistration';
|
|
57
59
|
import { getSynchronize } from '../utils/getSynchronize';
|
|
58
60
|
import { runMethodWithUnlockPolicy } from '../protocols/protocol-v2/unlockPolicyRunner';
|
|
@@ -1260,6 +1262,7 @@ const onDevicePinHandler = async (...[device, type, callback]: DeviceEvents['pin
|
|
|
1260
1262
|
createUiMessage(UI_REQUEST.REQUEST_PIN, {
|
|
1261
1263
|
device: device.toMessageObject() as unknown as KnownDevice,
|
|
1262
1264
|
type,
|
|
1265
|
+
responseCorrelation: uiPromise.responseCorrelation,
|
|
1263
1266
|
})
|
|
1264
1267
|
);
|
|
1265
1268
|
// wait for pin
|
|
@@ -1308,6 +1311,7 @@ const onDevicePassphraseHandler = async (
|
|
|
1308
1311
|
reason: requestPayload.reason,
|
|
1309
1312
|
expectedPassphraseState: requestPayload.expectedPassphraseState,
|
|
1310
1313
|
...(requestPayload.interaction ? { interaction: requestPayload.interaction } : {}),
|
|
1314
|
+
responseCorrelation: uiPromise.responseCorrelation,
|
|
1311
1315
|
})
|
|
1312
1316
|
);
|
|
1313
1317
|
// wait for passphrase
|
|
@@ -1431,14 +1435,21 @@ const postMessage = (message: CoreMessage) => {
|
|
|
1431
1435
|
|
|
1432
1436
|
const createUiPromise = <T extends UiPromiseResponse['type']>(promiseEvent: T, device?: Device) => {
|
|
1433
1437
|
const uiPromise: UiPromise<T> = createDeferred(promiseEvent, device);
|
|
1438
|
+
if (
|
|
1439
|
+
device &&
|
|
1440
|
+
(promiseEvent === UI_RESPONSE.RECEIVE_PIN || promiseEvent === UI_RESPONSE.RECEIVE_PASSPHRASE)
|
|
1441
|
+
) {
|
|
1442
|
+
const publicDeviceId = device.toMessageObject()?.deviceId;
|
|
1443
|
+
uiPromise.responseCorrelation = {
|
|
1444
|
+
interactionId: generateInstanceId('UiResponse', device.sdkInstanceId),
|
|
1445
|
+
deviceId: publicDeviceId || device.instanceId,
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1434
1448
|
_uiPromises.push(uiPromise as any);
|
|
1435
1449
|
|
|
1436
1450
|
return uiPromise;
|
|
1437
1451
|
};
|
|
1438
1452
|
|
|
1439
|
-
const findUiPromise = <T extends UiPromiseResponse['type']>(promiseEvent: T) =>
|
|
1440
|
-
_uiPromises.find(p => p.id === promiseEvent);
|
|
1441
|
-
|
|
1442
1453
|
const removeUiPromise = (promise: Deferred<any>) => {
|
|
1443
1454
|
_uiPromises = _uiPromises.filter(p => p !== promise);
|
|
1444
1455
|
};
|
|
@@ -1490,11 +1501,16 @@ export default class Core extends EventEmitter {
|
|
|
1490
1501
|
case UI_RESPONSE.RECEIVE_PASSPHRASE:
|
|
1491
1502
|
case UI_RESPONSE.SELECT_DEVICE_IN_BOOTLOADER_FOR_WEB_DEVICE:
|
|
1492
1503
|
case UI_RESPONSE.SELECT_DEVICE_FOR_SWITCH_FIRMWARE_WEB_DEVICE: {
|
|
1493
|
-
const uiPromise =
|
|
1504
|
+
const uiPromise = findUiPromiseForResponse(_uiPromises, message);
|
|
1494
1505
|
if (uiPromise) {
|
|
1495
1506
|
Log.log('receive UI Response: ', message.type);
|
|
1496
1507
|
uiPromise.resolve(message);
|
|
1497
1508
|
removeUiPromise(uiPromise);
|
|
1509
|
+
} else if (
|
|
1510
|
+
message.type === UI_RESPONSE.RECEIVE_PIN ||
|
|
1511
|
+
message.type === UI_RESPONSE.RECEIVE_PASSPHRASE
|
|
1512
|
+
) {
|
|
1513
|
+
Log.warn('Ignored unmatched or ambiguous sensitive UI response:', message.type);
|
|
1498
1514
|
}
|
|
1499
1515
|
break;
|
|
1500
1516
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { UI_RESPONSE } from '../events/ui-response';
|
|
2
|
+
|
|
3
|
+
import type { UiPromise, UiPromiseResponse } from '../events/ui-promise';
|
|
4
|
+
import type { UiResponseEvent } from '../events/ui-response';
|
|
5
|
+
|
|
6
|
+
const isSensitiveUiResponse = (
|
|
7
|
+
response: UiResponseEvent
|
|
8
|
+
): response is Extract<
|
|
9
|
+
UiResponseEvent,
|
|
10
|
+
{ type: typeof UI_RESPONSE.RECEIVE_PIN | typeof UI_RESPONSE.RECEIVE_PASSPHRASE }
|
|
11
|
+
> => response.type === UI_RESPONSE.RECEIVE_PIN || response.type === UI_RESPONSE.RECEIVE_PASSPHRASE;
|
|
12
|
+
|
|
13
|
+
export const findUiPromiseForResponse = (
|
|
14
|
+
uiPromises: UiPromise<UiPromiseResponse['type']>[],
|
|
15
|
+
response: UiResponseEvent
|
|
16
|
+
) => {
|
|
17
|
+
const candidates = uiPromises.filter(promise => promise.id === response.type);
|
|
18
|
+
if (!isSensitiveUiResponse(response)) {
|
|
19
|
+
return candidates[0];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const hasInteractionId = typeof response.interactionId === 'string';
|
|
23
|
+
const hasDeviceId = typeof response.deviceId === 'string';
|
|
24
|
+
if (hasInteractionId !== hasDeviceId) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (hasInteractionId && hasDeviceId) {
|
|
29
|
+
return candidates.find(promise => {
|
|
30
|
+
const correlation = promise.responseCorrelation;
|
|
31
|
+
return (
|
|
32
|
+
correlation?.interactionId === response.interactionId &&
|
|
33
|
+
correlation?.deviceId === response.deviceId
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Legacy clients do not return correlation metadata. Preserve their single-request
|
|
39
|
+
// behavior, but never guess when multiple sensitive requests are pending.
|
|
40
|
+
return candidates.length === 1 ? candidates[0] : undefined;
|
|
41
|
+
};
|
|
@@ -509,10 +509,6 @@ export default class DataManager {
|
|
|
509
509
|
return this.deviceMap[EDeviceType.Pro2]?.resources?.stable;
|
|
510
510
|
}
|
|
511
511
|
|
|
512
|
-
static getProtocolV2BootResources() {
|
|
513
|
-
return this.deviceMap[EDeviceType.Pro2]?.resources?.boot;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
512
|
static getProtobufMessages(schema: ProtobufMessageSchema = 'v1CurrentSchema'): JSON {
|
|
517
513
|
return this.messages[schema];
|
|
518
514
|
}
|
package/src/events/ui-promise.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Deferred } from '@onekeyfe/hd-shared';
|
|
2
2
|
import type { DEVICE } from './device';
|
|
3
3
|
import type { Device } from '../device/Device';
|
|
4
|
-
import type { UiResponseEvent } from './ui-response';
|
|
4
|
+
import type { UiResponseCorrelation, UiResponseEvent } from './ui-response';
|
|
5
5
|
|
|
6
6
|
export type UiPromiseResponse =
|
|
7
7
|
| UiResponseEvent
|
|
@@ -11,4 +11,6 @@ export type UiPromise<T extends UiPromiseResponse['type']> = Deferred<
|
|
|
11
11
|
Extract<UiPromiseResponse, { type: T }>,
|
|
12
12
|
T,
|
|
13
13
|
Device
|
|
14
|
-
|
|
14
|
+
> & {
|
|
15
|
+
responseCorrelation?: UiResponseCorrelation;
|
|
16
|
+
};
|
package/src/events/ui-request.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PROTO } from '../constants';
|
|
2
2
|
import type { Device } from '../types';
|
|
3
3
|
import type { DeviceButtonRequest } from './device';
|
|
4
|
+
import type { UiResponseCorrelation } from './ui-response';
|
|
4
5
|
import type { MessageFactoryFn } from './utils';
|
|
5
6
|
|
|
6
7
|
export const UI_EVENT = 'UI_EVENT';
|
|
@@ -121,6 +122,7 @@ export type UiRequestDeviceAction = {
|
|
|
121
122
|
payload: ProtocolV2UiEventMetadata & {
|
|
122
123
|
device: Device;
|
|
123
124
|
type?: PROTO.PinMatrixRequestType | 'ButtonRequest_PinEntry' | 'ButtonRequest_AttachPin';
|
|
125
|
+
responseCorrelation?: UiResponseCorrelation;
|
|
124
126
|
};
|
|
125
127
|
};
|
|
126
128
|
|
|
@@ -140,6 +142,7 @@ export interface UiRequestPassphrase {
|
|
|
140
142
|
reason?: 'open-wallet' | 'session-recovery';
|
|
141
143
|
expectedPassphraseState?: string;
|
|
142
144
|
interaction?: HardwareUiInteractionMeta;
|
|
145
|
+
responseCorrelation?: UiResponseCorrelation;
|
|
143
146
|
};
|
|
144
147
|
}
|
|
145
148
|
|
|
@@ -11,12 +11,22 @@ export const UI_RESPONSE = {
|
|
|
11
11
|
'ui-receive_select-device-for-switch-firmware-web-device',
|
|
12
12
|
} as const;
|
|
13
13
|
|
|
14
|
-
export interface
|
|
14
|
+
export interface UiResponseCorrelation {
|
|
15
|
+
interactionId: string;
|
|
16
|
+
deviceId: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface UiResponseCorrelationFields {
|
|
20
|
+
interactionId?: string;
|
|
21
|
+
deviceId?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface UiResponsePin extends UiResponseCorrelationFields {
|
|
15
25
|
type: typeof UI_RESPONSE.RECEIVE_PIN;
|
|
16
26
|
payload: string;
|
|
17
27
|
}
|
|
18
28
|
|
|
19
|
-
export interface UiResponsePassphrase {
|
|
29
|
+
export interface UiResponsePassphrase extends UiResponseCorrelationFields {
|
|
20
30
|
type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE;
|
|
21
31
|
payload: {
|
|
22
32
|
value: string;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { sha256 } from '@noble/hashes/sha256';
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
-
IProtocolV2BootResources,
|
|
5
4
|
IProtocolV2Resource,
|
|
6
5
|
IProtocolV2ResourceType,
|
|
7
6
|
IProtocolV2Resources,
|
|
@@ -150,34 +149,6 @@ function validateResource(value: unknown, index: number): IProtocolV2Resource {
|
|
|
150
149
|
};
|
|
151
150
|
}
|
|
152
151
|
|
|
153
|
-
function validateBootResources(value: unknown): IProtocolV2BootResources {
|
|
154
|
-
if (!value || typeof value !== 'object') {
|
|
155
|
-
throw new Error('Invalid Pro2 boot resources config');
|
|
156
|
-
}
|
|
157
|
-
const resource = value as Partial<IProtocolV2BootResources>;
|
|
158
|
-
if (resource.required !== false) {
|
|
159
|
-
throw new Error('Invalid Pro2 boot resources required flag: expected false');
|
|
160
|
-
}
|
|
161
|
-
if (resource.target !== 'CRATE') {
|
|
162
|
-
throw new Error('Invalid Pro2 boot resources target: expected CRATE');
|
|
163
|
-
}
|
|
164
|
-
if (typeof resource.url !== 'string' || !resource.url.startsWith('https://')) {
|
|
165
|
-
throw new Error('Invalid Pro2 boot resources url');
|
|
166
|
-
}
|
|
167
|
-
if (!Number.isSafeInteger(resource.size) || Number(resource.size) <= 0) {
|
|
168
|
-
throw new Error('Invalid Pro2 boot resources size');
|
|
169
|
-
}
|
|
170
|
-
return {
|
|
171
|
-
required: false,
|
|
172
|
-
target: 'CRATE',
|
|
173
|
-
url: resource.url,
|
|
174
|
-
size: Number(resource.size),
|
|
175
|
-
fileHash: normalizeHex(resource.fileHash, SHA256_HEX_LENGTH, 'boot fileHash'),
|
|
176
|
-
payloadHash: normalizeHex(resource.payloadHash, SHA3_512_HEX_LENGTH, 'boot payloadHash'),
|
|
177
|
-
headerHash: normalizeHex(resource.headerHash, SHA3_512_HEX_LENGTH, 'boot headerHash'),
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
152
|
/** Validate a complete Pro2 stable resource set from remote configuration. */
|
|
182
153
|
export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources | undefined {
|
|
183
154
|
if (value === undefined) return undefined;
|
|
@@ -189,8 +160,7 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
|
|
|
189
160
|
throw new Error('Invalid Pro2 resources config: stable must be an array');
|
|
190
161
|
}
|
|
191
162
|
|
|
192
|
-
const
|
|
193
|
-
const stable = config.stable.map(validateResource);
|
|
163
|
+
const stable = (value as { stable: unknown[] }).stable.map(validateResource);
|
|
194
164
|
const types = new Set(stable.map(resource => resource.type));
|
|
195
165
|
if (stable.length !== PROTOCOL_V2_RESOURCE_TYPES.length || types.size !== stable.length) {
|
|
196
166
|
throw new Error('Invalid Pro2 resources config: stable must contain six unique resource types');
|
|
@@ -200,7 +170,6 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
|
|
|
200
170
|
throw new Error(`Invalid Pro2 resources config: stable is missing ${type}`);
|
|
201
171
|
}
|
|
202
172
|
}
|
|
203
|
-
const boot = config.boot === undefined ? undefined : validateBootResources(config.boot);
|
|
204
173
|
return {
|
|
205
174
|
stable: PROTOCOL_V2_RESOURCE_TYPES.map(type => {
|
|
206
175
|
const resource = stable.find(item => item.type === type);
|
|
@@ -209,7 +178,6 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
|
|
|
209
178
|
}
|
|
210
179
|
return resource;
|
|
211
180
|
}),
|
|
212
|
-
...(boot ? { boot } : undefined),
|
|
213
181
|
};
|
|
214
182
|
}
|
|
215
183
|
|
|
@@ -164,9 +164,13 @@ const selectDeviceSession = async (
|
|
|
164
164
|
return getDeviceSession(device, buildDeviceSessionGetRequest({ deriveCardano }));
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
const passphraseOnDeviceInteraction = device.createProtocolV2UiPhaseMetadata?.(
|
|
168
|
+
'passphrase-on-device',
|
|
169
|
+
'start'
|
|
170
|
+
);
|
|
167
171
|
device.emit(DEVICE.PASSPHRASE_ON_DEVICE, device, {
|
|
168
172
|
...metadata,
|
|
169
|
-
...(
|
|
173
|
+
...(passphraseOnDeviceInteraction ? { interaction: passphraseOnDeviceInteraction } : {}),
|
|
170
174
|
});
|
|
171
175
|
await askDevicePassphrase(device, { on_device: true });
|
|
172
176
|
return getDeviceSession(device, buildDeviceSessionGetRequest({ deriveCardano }));
|
|
@@ -63,7 +63,6 @@ export interface FirmwareUpdateV3Params {
|
|
|
63
63
|
*/
|
|
64
64
|
export type FirmwareUpdateV4Target =
|
|
65
65
|
| 'boot'
|
|
66
|
-
| 'boot_resources'
|
|
67
66
|
| 'app_v1'
|
|
68
67
|
| 'app_v2'
|
|
69
68
|
| 'coprocessor'
|
|
@@ -83,8 +82,6 @@ export interface FirmwareUpdateV4Params {
|
|
|
83
82
|
romloaderBinary?: ArrayBuffer;
|
|
84
83
|
/** FW_MGMT_TARGET_BOOTLOADER = 3 */
|
|
85
84
|
bootloaderBinary?: ArrayBuffer;
|
|
86
|
-
/** FW_MGMT_TARGET_CRATE = 1; optional Pro2 startup resources. */
|
|
87
|
-
bootResourcesBinary?: ArrayBuffer;
|
|
88
85
|
/** FW_MGMT_TARGET_APPLICATION_P1 = 4 */
|
|
89
86
|
applicationP1Binary?: ArrayBuffer;
|
|
90
87
|
/** FW_MGMT_TARGET_APPLICATION_P2 = 5 */
|
package/src/types/settings.ts
CHANGED
|
@@ -81,24 +81,8 @@ export type IProtocolV2Resource = {
|
|
|
81
81
|
headerHash: string;
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
/** Optional Pro2 startup-resource CRATE installed by the bootloader updater. */
|
|
85
|
-
export type IProtocolV2BootResources = {
|
|
86
|
-
required: false;
|
|
87
|
-
target: 'CRATE';
|
|
88
|
-
url: string;
|
|
89
|
-
/** Complete CRATE file size in bytes. */
|
|
90
|
-
size: number;
|
|
91
|
-
/** SHA-256 of the complete CRATE file. */
|
|
92
|
-
fileHash: string;
|
|
93
|
-
/** SHA3-512 payload_hash from the signed CRATE header. */
|
|
94
|
-
payloadHash: string;
|
|
95
|
-
/** SHA3-512 header_hash from the signed CRATE header. */
|
|
96
|
-
headerHash: string;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
84
|
export type IProtocolV2Resources = {
|
|
100
85
|
stable: IProtocolV2Resource[];
|
|
101
|
-
boot?: IProtocolV2BootResources;
|
|
102
86
|
};
|
|
103
87
|
|
|
104
88
|
/** Pro2 RESC bundle okpkg descriptor for incremental FileWrite synchronization. */
|
package/src/utils/pro2Nft.ts
CHANGED
|
@@ -9,8 +9,8 @@ export const PRO2_NFT_IMAGE_HEIGHT = 540;
|
|
|
9
9
|
export const PRO2_NFT_THUMBNAIL_WIDTH = 263;
|
|
10
10
|
export const PRO2_NFT_THUMBNAIL_HEIGHT = 263;
|
|
11
11
|
export const PRO2_NFT_DIRECTORY = 'vol1:/nft';
|
|
12
|
-
export const PRO2_NFT_DEFAULT_CHUNK_SIZE =
|
|
13
|
-
export const PRO2_NFT_DEFAULT_PACE_MS =
|
|
12
|
+
export const PRO2_NFT_DEFAULT_CHUNK_SIZE = 2048;
|
|
13
|
+
export const PRO2_NFT_DEFAULT_PACE_MS = 0;
|
|
14
14
|
export const PRO2_NFT_DEFAULT_TIMEOUT_MS = 15_000;
|
|
15
15
|
export const PRO2_NFT_MIN_CHUNK_SIZE = 64;
|
|
16
16
|
export const PRO2_NFT_MAX_CHUNK_SIZE = 2048;
|