@onekeyfe/hd-core 1.2.0-alpha.135 → 1.2.0-alpha.137
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__/DeviceCommands.test.ts +1 -1
- package/__tests__/device-lifecycle-events.test.ts +22 -24
- package/__tests__/device-wallet-session-store.test.ts +147 -21
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +1 -37
- package/__tests__/protocol-v2-unlock-policy.test.ts +0 -59
- package/__tests__/protocol-v2.test.ts +20 -52
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/device/DeviceChangePin.d.ts.map +1 -1
- package/dist/api/device/DeviceVerify.d.ts +0 -1
- package/dist/api/device/DeviceVerify.d.ts.map +1 -1
- package/dist/api/device/DeviceWipe.d.ts.map +1 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +68 -39
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +68 -25
- package/src/api/device/DeviceChangePin.ts +1 -4
- package/src/api/device/DeviceVerify.ts +0 -9
- package/src/api/device/DeviceWipe.ts +1 -4
- package/src/device/Device.ts +57 -14
- package/src/device/DeviceCommands.ts +1 -1
- package/src/device/DeviceConnector.ts +4 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { sha256 } from '@noble/hashes/sha256';
|
|
2
2
|
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
|
-
import { DeviceSessionPinType } from '@onekeyfe/hd-transport';
|
|
4
3
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
5
4
|
|
|
6
5
|
import { formatAnyHex } from '../helpers/hexUtils';
|
|
@@ -12,15 +11,7 @@ import type { BixinVerifyDeviceRequest } from '@onekeyfe/hd-transport';
|
|
|
12
11
|
import type { DeviceVerifySignature } from '../../types';
|
|
13
12
|
|
|
14
13
|
export default class DeviceVerify extends BaseMethod<BixinVerifyDeviceRequest> {
|
|
15
|
-
getSupportedProtocols() {
|
|
16
|
-
return ['V1', 'V2'] as const;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
14
|
init() {
|
|
20
|
-
this.unlockPolicy = 'unlock-before-run';
|
|
21
|
-
// Protocol V2 device-management actions are not wallet-scoped, so either
|
|
22
|
-
// the main PIN or an Attach PIN may authorize them.
|
|
23
|
-
this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
|
|
24
15
|
this.useDevicePassphraseState = false;
|
|
25
16
|
|
|
26
17
|
// check payload
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DeviceSettingsPage } from '@onekeyfe/hd-transport';
|
|
2
2
|
|
|
3
3
|
import { BaseMethod } from '../BaseMethod';
|
|
4
4
|
|
|
@@ -11,9 +11,6 @@ export default class DeviceWipe extends BaseMethod<WipeDevice> {
|
|
|
11
11
|
|
|
12
12
|
init() {
|
|
13
13
|
this.unlockPolicy = 'unlock-before-run';
|
|
14
|
-
// Protocol V2 device-management actions are not wallet-scoped, so either
|
|
15
|
-
// the main PIN or an Attach PIN may authorize them.
|
|
16
|
-
this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
|
|
17
14
|
this.useDevicePassphraseState = false;
|
|
18
15
|
this.protocolV2UiInteraction = {
|
|
19
16
|
request: 'button',
|
package/src/device/Device.ts
CHANGED
|
@@ -449,7 +449,8 @@ export class Device extends EventEmitter {
|
|
|
449
449
|
undefined,
|
|
450
450
|
true,
|
|
451
451
|
strictProtocol,
|
|
452
|
-
undefined
|
|
452
|
+
undefined,
|
|
453
|
+
options?.forceProtocolDetection
|
|
453
454
|
);
|
|
454
455
|
this.mainId = (acquireResult as any)?.uuid ?? '';
|
|
455
456
|
Log.debug('Expected uuid:', this.mainId);
|
|
@@ -459,7 +460,8 @@ export class Device extends EventEmitter {
|
|
|
459
460
|
this.originalDescriptor.session,
|
|
460
461
|
undefined,
|
|
461
462
|
strictProtocol,
|
|
462
|
-
undefined
|
|
463
|
+
undefined,
|
|
464
|
+
options?.forceProtocolDetection
|
|
463
465
|
);
|
|
464
466
|
this.mainId = acquireResult as string | undefined;
|
|
465
467
|
Log.debug('Expected session id:', this.mainId);
|
|
@@ -930,13 +932,18 @@ export class Device extends EventEmitter {
|
|
|
930
932
|
};
|
|
931
933
|
|
|
932
934
|
const expectedDeviceId = options?.deviceId;
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
//
|
|
935
|
+
|
|
936
|
+
if (expectedDeviceId && !(this.features && this.checkDeviceId(expectedDeviceId))) {
|
|
937
|
+
// No locally-cached evidence that the device at this path is the
|
|
938
|
+
// expected one (first contact, or the cached features already disagree
|
|
939
|
+
// with the caller). Establish identity with a context-free Initialize
|
|
940
|
+
// BEFORE any wallet context (session_id / passphrase_state) goes on
|
|
941
|
+
// the wire. In normal flows features are always fresh — enumerate /
|
|
942
|
+
// getFeatures run first and every call response refreshes them — so
|
|
943
|
+
// this extra round trip is confined to the ambiguous cases where the
|
|
944
|
+
// disclosure risk actually lives.
|
|
936
945
|
this.passphraseState = undefined;
|
|
937
|
-
|
|
938
|
-
this._updateFeatures(message);
|
|
939
|
-
await TransportManager.reconfigure(this.features);
|
|
946
|
+
await callInitialize({ is_contains_attach: true });
|
|
940
947
|
if (!this.checkDeviceId(expectedDeviceId)) {
|
|
941
948
|
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
942
949
|
}
|
|
@@ -960,7 +967,46 @@ export class Device extends EventEmitter {
|
|
|
960
967
|
payload.derive_cardano = true;
|
|
961
968
|
}
|
|
962
969
|
|
|
963
|
-
|
|
970
|
+
if (this.features) {
|
|
971
|
+
// Re-sync the V1 message schema for THIS device before encoding
|
|
972
|
+
// Initialize: the process-global schema may still reflect another
|
|
973
|
+
// device (e.g. legacy-firmware Touch/Mini) on multi-device setups, and
|
|
974
|
+
// a stale legacy schema would silently strip passphrase_state /
|
|
975
|
+
// is_contains_attach from the wire message. Local operation, no wire
|
|
976
|
+
// I/O; a no-op when the schema is unchanged.
|
|
977
|
+
await TransportManager.reconfigure(this.features);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// Initialize's own Features response carries device_id, so the physical
|
|
981
|
+
// device identity is validated on the same round trip instead of via a
|
|
982
|
+
// separate read-only GetFeatures preflight (which doubled the wire cost
|
|
983
|
+
// of every deviceId-carrying call). The method fn has not run yet, so a
|
|
984
|
+
// mismatch still fails before any wallet data can be derived, with the
|
|
985
|
+
// same DeviceCheckDeviceIdError. Wallet-context selection is unchanged:
|
|
986
|
+
// it is decided by the Initialize payload above either way.
|
|
987
|
+
const assertExpectedDeviceIdentity = () => {
|
|
988
|
+
if (expectedDeviceId && !this.checkDeviceId(expectedDeviceId)) {
|
|
989
|
+
// The mismatched Initialize may have cached a session under the wrong
|
|
990
|
+
// device's identity; drop it so no wallet context survives from it.
|
|
991
|
+
// (This also evicts any session the wrong device legitimately cached
|
|
992
|
+
// under the same passphraseState — a deliberate conservative purge
|
|
993
|
+
// after a physical-swap event, consistent with
|
|
994
|
+
// reconcileDeviceIdentity purging the previous device's sessions.)
|
|
995
|
+
this.clearInternalState();
|
|
996
|
+
throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckDeviceIdError);
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
try {
|
|
1001
|
+
await callInitialize(payload, options?.initSession);
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
// callInitialize can fail AFTER the wire call cached the session (e.g.
|
|
1004
|
+
// TransportManager.reconfigure rejecting); the identity check must
|
|
1005
|
+
// still run so a wrong-device session never survives the error path.
|
|
1006
|
+
assertExpectedDeviceIdentity();
|
|
1007
|
+
throw error;
|
|
1008
|
+
}
|
|
1009
|
+
assertExpectedDeviceIdentity();
|
|
964
1010
|
} catch (error) {
|
|
965
1011
|
Log.error('Initialization failed:', error);
|
|
966
1012
|
throw error;
|
|
@@ -1530,18 +1576,15 @@ export class Device extends EventEmitter {
|
|
|
1530
1576
|
const error = ERRORS.TypedError(HardwareErrorCode.DeviceInterruptedFromUser);
|
|
1531
1577
|
const cleanupPromise = this.runCleanupPromise;
|
|
1532
1578
|
const { cancelableAction } = this;
|
|
1533
|
-
const env = DataManager.getSettings('env');
|
|
1534
1579
|
if (cancelableAction) {
|
|
1535
1580
|
await cancelableAction(error);
|
|
1536
1581
|
} else if (
|
|
1537
1582
|
this.isProtocolV2() &&
|
|
1538
|
-
|
|
1539
|
-
DataManager.isBrowserWebUsb(env) ||
|
|
1540
|
-
DataManager.isDesktopWebUsb(env)) &&
|
|
1583
|
+
DataManager.isBleConnect(DataManager.getSettings('env')) &&
|
|
1541
1584
|
this.hasDeviceAcquire()
|
|
1542
1585
|
) {
|
|
1543
1586
|
await this.commands?.cancelDevice?.().catch(cancelError => {
|
|
1544
|
-
Log.debug('Protocol V2 fallback cancel error', cancelError);
|
|
1587
|
+
Log.debug('Protocol V2 BLE fallback cancel error', cancelError);
|
|
1545
1588
|
});
|
|
1546
1589
|
}
|
|
1547
1590
|
await this.commands?.cancel();
|
|
@@ -69,7 +69,7 @@ const DEVICE_CONTROL_CALLS = new Set([
|
|
|
69
69
|
// Older Protocol V2 firmware may omit the cancellation subcode, so retain a
|
|
70
70
|
// narrow message fallback for compatibility.
|
|
71
71
|
const isProtocolV2ActionCancelledMessage = (message: string) =>
|
|
72
|
-
/^(?:cancel(?:led|ed)(?: on device)?|confirm dismissed|
|
|
72
|
+
/^(?:cancel(?:led|ed)(?: on device)?|confirm dismissed|user cancel(?:led|ed)(?:\s+.*)?)$/i.test(
|
|
73
73
|
message
|
|
74
74
|
);
|
|
75
75
|
|
|
@@ -95,7 +95,8 @@ export default class DeviceConnector {
|
|
|
95
95
|
session?: string | null,
|
|
96
96
|
forceCleanRunPromise?: boolean,
|
|
97
97
|
expectedProtocol?: HardwareConnectProtocol,
|
|
98
|
-
protocolHint?: HardwareConnectProtocol
|
|
98
|
+
protocolHint?: HardwareConnectProtocol,
|
|
99
|
+
forceProtocolDetection?: boolean
|
|
99
100
|
) {
|
|
100
101
|
Log.debug('acquire', path, session, expectedProtocol, protocolHint);
|
|
101
102
|
const env = DataManager.getSettings('env');
|
|
@@ -108,6 +109,7 @@ export default class DeviceConnector {
|
|
|
108
109
|
forceCleanRunPromise,
|
|
109
110
|
expectedProtocol,
|
|
110
111
|
protocolHint,
|
|
112
|
+
forceProtocolDetection,
|
|
111
113
|
});
|
|
112
114
|
} else {
|
|
113
115
|
res = await transport.acquire({
|
|
@@ -115,6 +117,7 @@ export default class DeviceConnector {
|
|
|
115
117
|
previous: session ?? null,
|
|
116
118
|
expectedProtocol,
|
|
117
119
|
protocolHint,
|
|
120
|
+
forceProtocolDetection,
|
|
118
121
|
});
|
|
119
122
|
}
|
|
120
123
|
if (expectedProtocol) {
|