@onekeyfe/hd-core 1.2.2-alpha.109 → 1.2.2-alpha.110

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.
@@ -15,12 +15,17 @@ import {
15
15
  } from '../src/core';
16
16
  import { DataManager } from '../src/data-manager';
17
17
  import GetDeviceState from '../src/api/GetDeviceState';
18
+ import DeviceVerify from '../src/api/device/DeviceVerify';
18
19
  import TransportManager from '../src/data-manager/TransportManager';
19
20
  import { Device } from '../src/device/Device';
20
21
  import { cancelDeviceInPrompt, cancelDeviceWithInitialize } from '../src/device/DeviceCommands';
21
22
  import { DevicePool } from '../src/device/DevicePool';
22
23
  import { CORE_EVENT, DEVICE, IFRAME } from '../src/events';
23
24
  import { PROTOCOL_V2_DEVICE_STATUS_GET_MESSAGE_TYPE } from '../src/protocols/protocol-v2';
25
+ import {
26
+ ProtocolV2UiInteractionCoordinator,
27
+ resolveProtocolV2UiInteraction,
28
+ } from '../src/protocols/protocol-v2/uiInteraction';
24
29
 
25
30
  import type Core from '../src/core';
26
31
  import type { CoreMessage } from '../src/events';
@@ -776,6 +781,38 @@ describe('public device lifecycle events', () => {
776
781
  }
777
782
  );
778
783
 
784
+ test.each(['react-native', 'desktop-web-ble', 'webusb', 'desktop-webusb'] as const)(
785
+ 'sends Cancel once for device verification on an already unlocked Protocol V2 %s device',
786
+ async env => {
787
+ jest.spyOn(DataManager, 'getSettings').mockReturnValue(env as never);
788
+ const device = createInitializedDevice('V2');
789
+ device.originalDescriptor.session = device.mainId;
790
+ jest.spyOn(device, 'hasDeviceAcquire').mockReturnValue(true);
791
+ const post = jest.fn().mockResolvedValue(undefined);
792
+ const cancel = jest.fn().mockResolvedValue(undefined);
793
+ device.commands = {
794
+ transport: { post },
795
+ cancelDevice: () => cancelDeviceInPrompt(device, false),
796
+ cancel,
797
+ } as never;
798
+ const method = new DeviceVerify({
799
+ payload: { method: 'deviceVerify', dataHex: '00' },
800
+ });
801
+ method.init();
802
+ const postMessage = jest.fn();
803
+ const coordinator = new ProtocolV2UiInteractionCoordinator(device, postMessage);
804
+ device.beginProtocolV2UiInteraction();
805
+ coordinator.enterMethodInteraction(resolveProtocolV2UiInteraction(method));
806
+
807
+ await device.interruptionFromUser();
808
+ await device.interruptionFromUser();
809
+
810
+ expect(post).toHaveBeenCalledTimes(1);
811
+ expect(post).toHaveBeenCalledWith(device.mainId, 'Cancel', {});
812
+ expect(cancel).toHaveBeenCalledTimes(1);
813
+ }
814
+ );
815
+
779
816
  test.each(['react-native', 'webusb', 'desktop-webusb'] as const)(
780
817
  'sends a fallback Cancel for an acquired Protocol V2 %s call with an open UI',
781
818
  async env => {
@@ -1,4 +1,5 @@
1
1
  import { Buffer } from 'buffer';
2
+ import { randomBytes } from 'crypto';
2
3
  import { EDeviceType, EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
3
4
  import { sha256 } from '@noble/hashes/sha256';
4
5
  import { bytesToHex } from '@noble/hashes/utils';
@@ -1824,6 +1825,56 @@ describe('Protocol V2 feature adapter', () => {
1824
1825
  ]);
1825
1826
  });
1826
1827
 
1828
+ test.each([false, true])(
1829
+ 'rejects the first incorrect passphrase after a missing or expired session (cached: %s)',
1830
+ async cached => {
1831
+ const deviceId = `first-passphrase-error-${String(cached)}`;
1832
+ const device = Device.fromDescriptor({
1833
+ id: deviceId,
1834
+ path: deviceId,
1835
+ protocolType: 'V2',
1836
+ } as never);
1837
+ device.features = normalizeProtocolV2Features(
1838
+ { ...descriptor, protocolType: 'V2' } as never,
1839
+ {
1840
+ status: { device_id: deviceId, unlocked: true, passphrase_enabled: true },
1841
+ }
1842
+ );
1843
+ device.passphraseState = 'expected-public-address';
1844
+ if (cached) {
1845
+ preloadSessionCache(deviceId, device.passphraseState, randomBytes(32).toString('hex'));
1846
+ }
1847
+ let reads = 0;
1848
+ const typedCall = createWalletSessionTypedCall(
1849
+ jest.fn((request: string) => {
1850
+ if (request === 'DeviceSessionAskPassphrase') return { message: {} };
1851
+ if (request === 'DeviceSessionGet') {
1852
+ reads += 1;
1853
+ if (reads === 1) {
1854
+ throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
1855
+ }
1856
+ return {
1857
+ message: {
1858
+ session_id: randomBytes(32).toString('hex'),
1859
+ btc_test_address: 'different-public-address',
1860
+ },
1861
+ };
1862
+ }
1863
+ throw new Error(`Unexpected request: ${request}`);
1864
+ })
1865
+ );
1866
+ const promptPassphrase = jest.fn().mockResolvedValue({ passphraseOnDevice: true });
1867
+ device.commands = { typedCall, promptPassphrase } as never;
1868
+
1869
+ await expect(getProtocolV2WalletSession(device)).rejects.toMatchObject({
1870
+ errorCode: HardwareErrorCode.DeviceCheckPassphraseStateError,
1871
+ });
1872
+ expect(promptPassphrase).toHaveBeenCalledTimes(1);
1873
+ expect(reads).toBe(2);
1874
+ expect(device.getInternalState()).toBeUndefined();
1875
+ }
1876
+ );
1877
+
1827
1878
  test('reselects a hidden wallet when the cached Pro2 session resolves to another wallet', async () => {
1828
1879
  const device = Device.fromDescriptor({
1829
1880
  id: 'cache-device-mismatch',
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceVerify.d.ts","sourceRoot":"","sources":["../../../src/api/device/DeviceVerify.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,UAAU,CAAC,wBAAwB,CAAC;IAC5E,qBAAqB;IAIrB,IAAI;IAgBE,GAAG;CAsDV"}
1
+ {"version":3,"file":"DeviceVerify.d.ts","sourceRoot":"","sources":["../../../src/api/device/DeviceVerify.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,UAAU,CAAC,wBAAwB,CAAC;IAC5E,qBAAqB;IAIrB,IAAI;IAwBE,GAAG;CAsDV"}
package/dist/index.js CHANGED
@@ -41878,6 +41878,7 @@ function getProtocolV2WalletSession(device, options) {
41878
41878
  ? device.getInternalState()
41879
41879
  : undefined;
41880
41880
  let response;
41881
+ let walletSelectionRequested = false;
41881
41882
  let resumed = false;
41882
41883
  let walletStatusRefreshed = false;
41883
41884
  const markWalletStatusRefreshed = () => {
@@ -42049,6 +42050,7 @@ function getProtocolV2WalletSession(device, options) {
42049
42050
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.WalletSessionInvalid);
42050
42051
  }
42051
42052
  yield lockAttachPinBeforePassphraseSelection();
42053
+ walletSelectionRequested = true;
42052
42054
  response = yield selectDeviceSession(device, expectedPassphraseState, options === null || options === void 0 ? void 0 : options.deriveCardano, markWalletStatusRefreshed);
42053
42055
  }
42054
42056
  let { message } = response;
@@ -42071,6 +42073,10 @@ function getProtocolV2WalletSession(device, options) {
42071
42073
  device.clearInternalState();
42072
42074
  throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.WalletSessionInvalid);
42073
42075
  }
42076
+ if (walletSelectionRequested) {
42077
+ clearCurrentWalletSession();
42078
+ throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.DeviceCheckPassphraseStateError);
42079
+ }
42074
42080
  if (options === null || options === void 0 ? void 0 : options.onlyMainPin) {
42075
42081
  (_j = device.clearStandardInternalState) === null || _j === void 0 ? void 0 : _j.call(device);
42076
42082
  yield selectStandardWallet(true);
@@ -49178,6 +49184,14 @@ class DeviceVerify extends BaseMethod {
49178
49184
  this.unlockPolicy = 'unlock-before-run';
49179
49185
  this.protocolV2PreUnlockPinType = hdTransport.DeviceSessionPinType.Any;
49180
49186
  this.useDevicePassphraseState = false;
49187
+ this.protocolV2UiInteraction = {
49188
+ request: 'button',
49189
+ source: 'method-lifecycle',
49190
+ reason: 'device-management',
49191
+ completion: 'operation-completed',
49192
+ deviceOnly: true,
49193
+ operation: 'deviceVerify',
49194
+ };
49181
49195
  validateParams(this.payload, [{ name: 'dataHex', type: 'hexString' }]);
49182
49196
  this.params = {
49183
49197
  data: formatAnyHex(this.payload.dataHex),
@@ -1 +1 @@
1
- {"version":3,"file":"walletSession.d.ts","sourceRoot":"","sources":["../../../src/protocols/protocol-v2/walletSession.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuBlD,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,MAAM,0DAGjE;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,MAAM,qCAGjE;AAED,wBAAsB,qCAAqC,CAAC,MAAM,EAAE,MAAM,oBAgBzE;AA8JD,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;;;;;;GAkYF;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,MAAM,EACd,uBAAuB,EAAE,MAAM,EAC/B,aAAa,CAAC,EAAE,OAAO;;;;;;GAOxB"}
1
+ {"version":3,"file":"walletSession.d.ts","sourceRoot":"","sources":["../../../src/protocols/protocol-v2/walletSession.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAuBlD,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,MAAM,0DAGjE;AAED,wBAAsB,6BAA6B,CAAC,MAAM,EAAE,MAAM,qCAGjE;AAED,wBAAsB,qCAAqC,CAAC,MAAM,EAAE,MAAM,oBAgBzE;AA8JD,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IACR,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;;;;;;GAyYF;AAED,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,MAAM,EACd,uBAAuB,EAAE,MAAM,EAC/B,aAAa,CAAC,EAAE,OAAO;;;;;;GAOxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-core",
3
- "version": "1.2.2-alpha.109",
3
+ "version": "1.2.2-alpha.110",
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.2-alpha.109",
29
- "@onekeyfe/hd-transport": "1.2.2-alpha.109",
28
+ "@onekeyfe/hd-shared": "1.2.2-alpha.110",
29
+ "@onekeyfe/hd-transport": "1.2.2-alpha.110",
30
30
  "axios": "1.15.2",
31
31
  "bignumber.js": "^9.0.2",
32
32
  "buffer": "^6.0.3",
@@ -46,5 +46,5 @@
46
46
  "@types/w3c-web-usb": "^1.0.10",
47
47
  "@types/web-bluetooth": "^0.0.21"
48
48
  },
49
- "gitHead": "4ee0588f08abdde1c72b8044091a76106e4770c4"
49
+ "gitHead": "80a959fb1e9383ce948b911fddece1265b40051f"
50
50
  }
@@ -22,6 +22,14 @@ export default class DeviceVerify extends BaseMethod<BixinVerifyDeviceRequest> {
22
22
  // the main PIN or an Attach PIN may authorize them.
23
23
  this.protocolV2PreUnlockPinType = DeviceSessionPinType.Any;
24
24
  this.useDevicePassphraseState = false;
25
+ this.protocolV2UiInteraction = {
26
+ request: 'button',
27
+ source: 'method-lifecycle',
28
+ reason: 'device-management',
29
+ completion: 'operation-completed',
30
+ deviceOnly: true,
31
+ operation: 'deviceVerify',
32
+ };
25
33
 
26
34
  // check payload
27
35
  validateParams(this.payload, [{ name: 'dataHex', type: 'hexString' }]);
@@ -268,6 +268,7 @@ export async function getProtocolV2WalletSession(
268
268
  ? device.getInternalState()
269
269
  : undefined;
270
270
  let response;
271
+ let walletSelectionRequested = false;
271
272
  let resumed = false;
272
273
  let walletStatusRefreshed = false;
273
274
  const markWalletStatusRefreshed = () => {
@@ -482,6 +483,7 @@ export async function getProtocolV2WalletSession(
482
483
  throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
483
484
  }
484
485
  await lockAttachPinBeforePassphraseSelection();
486
+ walletSelectionRequested = true;
485
487
  response = await selectDeviceSession(
486
488
  device,
487
489
  expectedPassphraseState,
@@ -508,6 +510,11 @@ export async function getProtocolV2WalletSession(
508
510
  device.clearInternalState();
509
511
  throw ERRORS.TypedError(HardwareErrorCode.WalletSessionInvalid);
510
512
  }
513
+ // Report a fresh selection mismatch before asking for input again.
514
+ if (walletSelectionRequested) {
515
+ clearCurrentWalletSession();
516
+ throw ERRORS.TypedError(HardwareErrorCode.DeviceCheckPassphraseStateError);
517
+ }
511
518
  if (options?.onlyMainPin) {
512
519
  device.clearStandardInternalState?.();
513
520
  await selectStandardWallet(true);