@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.
@@ -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 { DeviceSessionPinType, DeviceSettingsPage } from '@onekeyfe/hd-transport';
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',
@@ -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
- if (expectedDeviceId) {
934
- // 先只读校验物理设备身份;钱包上下文仍由下方携带完整参数的
935
- // Initialize 选择,避免标准钱包请求复用此前的隐藏钱包上下文。
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
- const { message } = await this.commands.typedCall('GetFeatures', 'Features', {});
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
- await callInitialize(payload, options?.initSession);
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
- (DataManager.isBleConnect(env) ||
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|update cancel(?:led|ed)|user cancel(?:led|ed)(?:\s+.*)?)$/i.test(
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) {