@onekeyfe/hd-shared 1.2.0-alpha.18 → 1.2.0-alpha.181

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/dist/index.js CHANGED
@@ -135,6 +135,7 @@ const HardwareErrorCode = {
135
135
  BleDeviceDisconnected: 720,
136
136
  BlePoweredOff: 721,
137
137
  BleUnsupported: 722,
138
+ BleUnavailableWhileUsbConnected: 723,
138
139
  RuntimeError: 800,
139
140
  PinInvalid: 801,
140
141
  PinCancelled: 802,
@@ -164,8 +165,9 @@ const HardwareErrorCode = {
164
165
  DeviceSettingsLanguageNotSupport: 826,
165
166
  TooManyInputs: 827,
166
167
  PinMismatch: 828,
167
- DeviceLocked: 829,
168
- KaspaPrevTxIdMismatch: 830,
168
+ DeviceLocked: 830,
169
+ WalletSessionInvalid: 831,
170
+ NftStorageLimitReached: 832,
169
171
  LowlevelTrasnportConnectError: 900,
170
172
  WebDeviceNotFoundOrNeedsPermission: 901,
171
173
  WebDevicePromptAccessError: 902,
@@ -241,6 +243,7 @@ const HardwareErrorCodeMessage = {
241
243
  [HardwareErrorCode.BleDeviceDisconnected]: 'Device disconnected',
242
244
  [HardwareErrorCode.BlePoweredOff]: 'Bluetooth is turned off',
243
245
  [HardwareErrorCode.BleUnsupported]: 'Bluetooth is not supported on this device',
246
+ [HardwareErrorCode.BleUnavailableWhileUsbConnected]: 'Bluetooth is unavailable while USB is connected. Unplug USB and try again.',
244
247
  [HardwareErrorCode.RuntimeError]: 'Runtime error',
245
248
  [HardwareErrorCode.PinInvalid]: 'Pin invalid',
246
249
  [HardwareErrorCode.PinCancelled]: 'Pin cancelled',
@@ -271,7 +274,8 @@ const HardwareErrorCodeMessage = {
271
274
  [HardwareErrorCode.TooManyInputs]: 'Too many inputs',
272
275
  [HardwareErrorCode.PinMismatch]: 'PIN mismatch',
273
276
  [HardwareErrorCode.DeviceLocked]: 'Device locked',
274
- [HardwareErrorCode.KaspaPrevTxIdMismatch]: 'Kaspa previous transaction id mismatch',
277
+ [HardwareErrorCode.WalletSessionInvalid]: 'Wallet session is invalid or expired',
278
+ [HardwareErrorCode.NftStorageLimitReached]: 'NFT storage limit reached. Remove an NFT from the device and try again.',
275
279
  [HardwareErrorCode.LowlevelTrasnportConnectError]: 'Lowlevel transport connect error',
276
280
  [HardwareErrorCode.WebDeviceNotFoundOrNeedsPermission]: 'Web-USB or Web-Bluetooth device not found or needs permission',
277
281
  [HardwareErrorCode.WebDevicePromptAccessError]: 'Web-USB or Web-Bluetooth device prompt access error',
@@ -395,6 +399,7 @@ const ONEKEY_WEBUSB_FILTER = [
395
399
  { vendorId: 0x1209, productId: 0x4f4b },
396
400
  { vendorId: 0x1209, productId: 0x4f4c },
397
401
  ];
402
+ const ONEKEY_PROTOCOL_V2_USB_IDS = [{ vendorId: 0x1209, productId: 0x4f4c }];
398
403
  const TREZOR_WEBUSB_MANUFACTURER_NAMES = new Set(['trezor', 'trezor company', 'satoshilabs']);
399
404
  const isKnownTrezorWebUsbDevice = (descriptor) => {
400
405
  var _a;
@@ -436,13 +441,20 @@ exports.EOneKeyBleMessageKeys = void 0;
436
441
  EOneKeyBleMessageKeys["NOBLE_BLE_STOP_SCAN"] = "$onekey-noble-ble-stop-scan";
437
442
  EOneKeyBleMessageKeys["NOBLE_BLE_GET_DEVICE"] = "$onekey-noble-ble-get-device";
438
443
  EOneKeyBleMessageKeys["NOBLE_BLE_CONNECT"] = "$onekey-noble-ble-connect";
444
+ EOneKeyBleMessageKeys["NOBLE_BLE_RELEASE"] = "$onekey-noble-ble-release";
439
445
  EOneKeyBleMessageKeys["NOBLE_BLE_DISCONNECT"] = "$onekey-noble-ble-disconnect";
440
446
  EOneKeyBleMessageKeys["NOBLE_BLE_WRITE"] = "$onekey-noble-ble-write";
441
447
  EOneKeyBleMessageKeys["NOBLE_BLE_SUBSCRIBE"] = "$onekey-noble-ble-subscribe";
442
448
  EOneKeyBleMessageKeys["NOBLE_BLE_UNSUBSCRIBE"] = "$onekey-noble-ble-unsubscribe";
443
449
  EOneKeyBleMessageKeys["NOBLE_BLE_NOTIFICATION"] = "$onekey-noble-ble-notification";
450
+ EOneKeyBleMessageKeys["NOBLE_BLE_MTU_CHANGED"] = "$onekey-noble-ble-mtu-changed";
444
451
  EOneKeyBleMessageKeys["NOBLE_BLE_CANCEL_PAIRING"] = "$onekey-noble-ble-cancel-pairing";
445
452
  })(exports.EOneKeyBleMessageKeys || (exports.EOneKeyBleMessageKeys = {}));
453
+ exports.EBleDisconnectReason = void 0;
454
+ (function (EBleDisconnectReason) {
455
+ EBleDisconnectReason["DeviceDisconnected"] = "device-disconnected";
456
+ EBleDisconnectReason["IdleKeepAlive"] = "idle-keep-alive";
457
+ })(exports.EBleDisconnectReason || (exports.EBleDisconnectReason = {}));
446
458
  const ONEKEY_SERVICE_UUID = '00000001-0000-1000-8000-00805f9b34fb';
447
459
  const ONEKEY_WRITE_CHARACTERISTIC_UUID = '00000002-0000-1000-8000-00805f9b34fb';
448
460
  const ONEKEY_NOTIFY_CHARACTERISTIC_UUID = '00000003-0000-1000-8000-00805f9b34fb';
@@ -489,11 +501,156 @@ const isOnekeyDevice = (name, id) => {
489
501
  return true;
490
502
  if (normalizedName.startsWith('touch ') ||
491
503
  normalizedName.startsWith('pro ') ||
492
- normalizedName.startsWith('pro2 ')) {
504
+ normalizedName.startsWith('pro2') ||
505
+ normalizedName.startsWith('neo')) {
506
+ return true;
507
+ }
508
+ const compactName = compactBleName(normalizedName);
509
+ if (PRO2_COMPACT_NAME_PATTERN.test(compactName) || NEO_COMPACT_NAME_PATTERN.test(compactName)) {
493
510
  return true;
494
511
  }
495
512
  return isOneKeyShortName(normalizedName);
496
513
  };
514
+ const inferProtocolHintFromUsbId = (vendorId, productId) => ONEKEY_PROTOCOL_V2_USB_IDS.some(id => id.vendorId === vendorId && id.productId === productId)
515
+ ? 'V2'
516
+ : undefined;
517
+ const resolveOneKeyUsbDevicePath = (device) => {
518
+ var _a, _b, _c, _d;
519
+ const serial = (_a = device.serialNumber) === null || _a === void 0 ? void 0 : _a.trim();
520
+ if (serial)
521
+ return serial;
522
+ if (device.vendorId == null && device.productId == null && !device.productName) {
523
+ return undefined;
524
+ }
525
+ const vendorId = ((_b = device.vendorId) !== null && _b !== void 0 ? _b : 0).toString(16).padStart(4, '0');
526
+ const productId = ((_c = device.productId) !== null && _c !== void 0 ? _c : 0).toString(16).padStart(4, '0');
527
+ const product = ((_d = device.productName) !== null && _d !== void 0 ? _d : 'onekey')
528
+ .trim()
529
+ .toLowerCase()
530
+ .replace(/\s+/g, '-')
531
+ .replace(/[^a-z0-9-]/g, '');
532
+ return `usb-${vendorId}-${productId}-${product || 'onekey'}`;
533
+ };
534
+ const BLUETOOTH_BASE_UUID_SUFFIX = '00001000800000805f9b34fb';
535
+ const normalizeBleUuid = (uuid) => (uuid !== null && uuid !== void 0 ? uuid : '').replace(/-/g, '').toLowerCase();
536
+ const createKnownBleUuidAliases = (uuid) => {
537
+ const normalized = normalizeBleUuid(uuid);
538
+ const aliases = new Set([normalized]);
539
+ if (normalized.length !== 32 || !normalized.endsWith(BLUETOOTH_BASE_UUID_SUFFIX)) {
540
+ return aliases;
541
+ }
542
+ const assignedNumber = normalized.slice(0, 8);
543
+ aliases.add(assignedNumber);
544
+ if (assignedNumber.startsWith('0000')) {
545
+ aliases.add(assignedNumber.slice(4));
546
+ }
547
+ return aliases;
548
+ };
549
+ const matchesKnownBleUuid = (actualUuid, aliases) => aliases.has(normalizeBleUuid(actualUuid));
550
+ const ONEKEY_COMMUNICATION_SERVICE_ALIASES = createKnownBleUuidAliases(ONEKEY_SERVICE_UUID);
551
+ const FIDO_SERVICE_ALIASES = createKnownBleUuidAliases('0000fffd-0000-1000-8000-00805f9b34fb');
552
+ const PRO2_COMPACT_NAME_PATTERN = /^(?:onekey)?pro2[a-f0-9]{4}$/i;
553
+ const NEO_COMPACT_NAME_PATTERN = /^(?:onekey)?neo[a-f0-9]{4}$/i;
554
+ const FIND_MY_COMPACT_SUFFIXES = [
555
+ 'findemy',
556
+ 'findem',
557
+ 'finde',
558
+ 'findmy',
559
+ 'findm',
560
+ 'find',
561
+ 'fin',
562
+ ];
563
+ const COMPLETE_FIND_MY_COMPACT_SUFFIXES = ['findemy', 'findmy'];
564
+ const compactBleName = (value) => Array.from(value)
565
+ .filter(character => character !== '-' && character.trim() !== '')
566
+ .join('')
567
+ .toLowerCase();
568
+ const getFindMyCompactSuffix = (compactName) => FIND_MY_COMPACT_SUFFIXES.find(candidate => compactName.endsWith(candidate));
569
+ const getPro2FindMyBaseLength = (value) => {
570
+ const compactName = compactBleName(value);
571
+ const suffix = getFindMyCompactSuffix(compactName);
572
+ if (!suffix)
573
+ return undefined;
574
+ const compactBaseName = compactName.slice(0, -suffix.length);
575
+ return PRO2_COMPACT_NAME_PATTERN.test(compactBaseName) ? compactBaseName.length : undefined;
576
+ };
577
+ const isPro2FindMyAdvertisementName = (value) => {
578
+ if (!value)
579
+ return false;
580
+ if (getPro2FindMyBaseLength(value) !== undefined)
581
+ return true;
582
+ const compactName = compactBleName(value);
583
+ const suffix = COMPLETE_FIND_MY_COMPACT_SUFFIXES.find(candidate => compactName.endsWith(candidate));
584
+ if (!suffix)
585
+ return false;
586
+ return compactName.slice(0, -suffix.length).includes('pro2');
587
+ };
588
+ const normalizePro2FindMyAdvertisementName = (value) => {
589
+ const baseLength = getPro2FindMyBaseLength(value);
590
+ if (baseLength === undefined)
591
+ return value;
592
+ let compactLength = 0;
593
+ for (let index = 0; index < value.length; index += 1) {
594
+ const character = value[index];
595
+ if (character !== '-' && character.trim() !== '') {
596
+ compactLength += 1;
597
+ if (compactLength === baseLength) {
598
+ return value.slice(0, index + 1).trimEnd();
599
+ }
600
+ }
601
+ }
602
+ return value;
603
+ };
604
+ const canonicalizePro2BleAdvertisementName = (value) => {
605
+ const withoutFindMy = normalizePro2FindMyAdvertisementName(value);
606
+ const compact = compactBleName(withoutFindMy);
607
+ if (!PRO2_COMPACT_NAME_PATTERN.test(compact))
608
+ return withoutFindMy;
609
+ const match = withoutFindMy.match(/^(onekey\s*)?pro\s*2\s*/i);
610
+ if (!match)
611
+ return withoutFindMy;
612
+ const rest = withoutFindMy.slice(match[0].length);
613
+ const prefix = match[1] ? 'OneKey Pro 2' : 'Pro 2';
614
+ return rest ? `${prefix} ${rest}` : prefix;
615
+ };
616
+ const isSameOnekeyBleName = (left, right) => {
617
+ if (!left || !right)
618
+ return false;
619
+ if (left === right)
620
+ return true;
621
+ return (compactBleName(canonicalizePro2BleAdvertisementName(left)) ===
622
+ compactBleName(canonicalizePro2BleAdvertisementName(right)));
623
+ };
624
+ const hasOnekeyCommunicationService = (serviceUuids) => (serviceUuids !== null && serviceUuids !== void 0 ? serviceUuids : []).some(uuid => matchesKnownBleUuid(uuid, ONEKEY_COMMUNICATION_SERVICE_ALIASES));
625
+ const isPro2FamilyBleName = (value) => {
626
+ if (!value) {
627
+ return false;
628
+ }
629
+ const normalized = value.trim().toLowerCase();
630
+ if (!normalized) {
631
+ return false;
632
+ }
633
+ if (normalized.startsWith('pro2') || normalized.startsWith('neo')) {
634
+ return true;
635
+ }
636
+ const compact = compactBleName(normalized);
637
+ return (PRO2_COMPACT_NAME_PATTERN.test(compact) ||
638
+ NEO_COMPACT_NAME_PATTERN.test(compact) ||
639
+ isPro2FindMyAdvertisementName(value));
640
+ };
641
+ const isOnekeyBluetoothDevice = ({ id, name, localName, serviceUuids, }) => {
642
+ const advertisedServiceUuids = serviceUuids !== null && serviceUuids !== void 0 ? serviceUuids : [];
643
+ if (hasOnekeyCommunicationService(advertisedServiceUuids)) {
644
+ return true;
645
+ }
646
+ if (isPro2FindMyAdvertisementName(name) || isPro2FindMyAdvertisementName(localName)) {
647
+ return false;
648
+ }
649
+ if (advertisedServiceUuids.some(uuid => matchesKnownBleUuid(uuid, FIDO_SERVICE_ALIASES))) {
650
+ return false;
651
+ }
652
+ return isOnekeyDevice(name !== null && name !== void 0 ? name : null, id) || isOnekeyDevice(localName !== null && localName !== void 0 ? localName : null, id);
653
+ };
497
654
 
498
655
  /******************************************************************************
499
656
  Copyright (c) Microsoft Corporation.
@@ -566,6 +723,7 @@ exports.EDeviceType = void 0;
566
723
  EDeviceType["Touch"] = "touch";
567
724
  EDeviceType["Pro"] = "pro";
568
725
  EDeviceType["Pro2"] = "pro2";
726
+ EDeviceType["Neo"] = "neo";
569
727
  })(exports.EDeviceType || (exports.EDeviceType = {}));
570
728
 
571
729
  exports.CreateErrorByMessage = CreateErrorByMessage;
@@ -578,19 +736,32 @@ exports.HardwareError = HardwareError;
578
736
  exports.HardwareErrorCode = HardwareErrorCode;
579
737
  exports.HardwareErrorCodeMessage = HardwareErrorCodeMessage;
580
738
  exports.ONEKEY_NOTIFY_CHARACTERISTIC_UUID = ONEKEY_NOTIFY_CHARACTERISTIC_UUID;
739
+ exports.ONEKEY_PROTOCOL_V2_USB_IDS = ONEKEY_PROTOCOL_V2_USB_IDS;
581
740
  exports.ONEKEY_SERVICE_UUID = ONEKEY_SERVICE_UUID;
582
741
  exports.ONEKEY_WEBUSB_FILTER = ONEKEY_WEBUSB_FILTER;
583
742
  exports.ONEKEY_WRITE_CHARACTERISTIC_UUID = ONEKEY_WRITE_CHARACTERISTIC_UUID;
584
743
  exports.TypedError = TypedError;
744
+ exports.canonicalizePro2BleAdvertisementName = canonicalizePro2BleAdvertisementName;
585
745
  exports.createDefectiveFirmwareError = createDefectiveFirmwareError;
586
746
  exports.createDeferred = createDeferred;
587
747
  exports.createDeprecatedHardwareError = createDeprecatedHardwareError;
588
748
  exports.createDeviceNotSupportMethodError = createDeviceNotSupportMethodError;
749
+ exports.createKnownBleUuidAliases = createKnownBleUuidAliases;
589
750
  exports.createNeedUpgradeFirmwareHardwareError = createNeedUpgradeFirmwareHardwareError;
590
751
  exports.createNewFirmwareForceUpdateHardwareError = createNewFirmwareForceUpdateHardwareError;
591
752
  exports.createNewFirmwareUnReleaseHardwareError = createNewFirmwareUnReleaseHardwareError;
753
+ exports.hasOnekeyCommunicationService = hasOnekeyCommunicationService;
754
+ exports.inferProtocolHintFromUsbId = inferProtocolHintFromUsbId;
592
755
  exports.isHeaderChunk = isHeaderChunk;
593
756
  exports.isKnownTrezorWebUsbDevice = isKnownTrezorWebUsbDevice;
757
+ exports.isOnekeyBluetoothDevice = isOnekeyBluetoothDevice;
594
758
  exports.isOnekeyDevice = isOnekeyDevice;
759
+ exports.isPro2FamilyBleName = isPro2FamilyBleName;
760
+ exports.isPro2FindMyAdvertisementName = isPro2FindMyAdvertisementName;
761
+ exports.isSameOnekeyBleName = isSameOnekeyBleName;
762
+ exports.matchesKnownBleUuid = matchesKnownBleUuid;
763
+ exports.normalizeBleUuid = normalizeBleUuid;
764
+ exports.normalizePro2FindMyAdvertisementName = normalizePro2FindMyAdvertisementName;
765
+ exports.resolveOneKeyUsbDevicePath = resolveOneKeyUsbDevicePath;
595
766
  exports.serializeError = serializeError;
596
767
  exports.wait = wait;
package/jest.config.js ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ preset: '../../jest.config.js',
3
+ testEnvironment: 'node',
4
+ modulePathIgnorePatterns: ['node_modules', '<rootDir>/dist'],
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/hd-shared",
3
- "version": "1.2.0-alpha.18",
3
+ "version": "1.2.0-alpha.181",
4
4
  "description": "Hardware SDK's shared tool library",
5
5
  "keywords": [
6
6
  "Hardware-SDK",
@@ -22,8 +22,9 @@
22
22
  "scripts": {
23
23
  "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w",
24
24
  "build": "rimraf dist && rollup -c ../../build/rollup.config.js",
25
+ "test": "jest",
25
26
  "lint": "eslint .",
26
27
  "lint:fix": "eslint . --fix"
27
28
  },
28
- "gitHead": "bdbc5bf74e48ca90f5c56f3079b2b974e61f7aa4"
29
+ "gitHead": "cb79652ebe130ceb01490195aa8903715f267e36"
29
30
  }
@@ -0,0 +1,36 @@
1
+ import {
2
+ HardwareErrorCode,
3
+ HardwareErrorCodeMessage,
4
+ TypedError,
5
+ serializeError,
6
+ } from './HardwareError';
7
+
8
+ describe('HardwareErrorCode compatibility', () => {
9
+ test('preserves published Protocol V2 values while leaving code 829 unused', () => {
10
+ expect(HardwareErrorCode).not.toHaveProperty('KaspaPrevTxIdMismatch');
11
+ expect(HardwareErrorCode.DeviceLocked).toBe(830);
12
+ expect(HardwareErrorCode.WalletSessionInvalid).toBe(831);
13
+ });
14
+
15
+ test('reserves the BLE and USB conflict code for app error mapping', () => {
16
+ expect(HardwareErrorCode.BleUnavailableWhileUsbConnected).toBe(723);
17
+ expect(HardwareErrorCodeMessage[HardwareErrorCode.BleUnavailableWhileUsbConnected]).toBe(
18
+ 'Bluetooth is unavailable while USB is connected. Unplug USB and try again.'
19
+ );
20
+ expect(
21
+ serializeError({
22
+ error: TypedError(HardwareErrorCode.BleUnavailableWhileUsbConnected, undefined, {
23
+ failureCode: 'Failure_ProcessError',
24
+ firmwareMessage: 'link disabled',
25
+ }),
26
+ })
27
+ ).toEqual({
28
+ code: 723,
29
+ error: 'Bluetooth is unavailable while USB is connected. Unplug USB and try again.',
30
+ params: {
31
+ failureCode: 'Failure_ProcessError',
32
+ firmwareMessage: 'link disabled',
33
+ },
34
+ });
35
+ });
36
+ });
@@ -345,6 +345,7 @@ export const HardwareErrorCode = {
345
345
  BleDeviceDisconnected: 720,
346
346
  BlePoweredOff: 721,
347
347
  BleUnsupported: 722,
348
+ BleUnavailableWhileUsbConnected: 723,
348
349
 
349
350
  /**
350
351
  * Hardware runtiome errors
@@ -488,12 +489,19 @@ export const HardwareErrorCode = {
488
489
  * Protocol V2 device must be unlocked before the requested operation.
489
490
  * @params: { failureCode?: string; subcode?: number; firmwareMessage?: string }
490
491
  */
491
- DeviceLocked: 829,
492
+ DeviceLocked: 830,
492
493
 
493
494
  /**
494
- * Device refused to sign: refTxs don't match the spent input's outpoint
495
+ * The cached wallet session is invalid or has expired.
496
+ * @params: { failureCode?: string; subcode?: number; firmwareMessage?: string }
497
+ */
498
+ WalletSessionInvalid: 831,
499
+
500
+ /**
501
+ * Pro2 NFT storage has reached its item limit.
502
+ * @params: { count: number; limit: number }
495
503
  */
496
- KaspaPrevTxIdMismatch: 830,
504
+ NftStorageLimitReached: 832,
497
505
 
498
506
  /**
499
507
  * Lowlevel transport connect error
@@ -612,6 +620,8 @@ export const HardwareErrorCodeMessage: HardwareErrorCodeMessageMapping = {
612
620
  [HardwareErrorCode.BleDeviceDisconnected]: 'Device disconnected',
613
621
  [HardwareErrorCode.BlePoweredOff]: 'Bluetooth is turned off',
614
622
  [HardwareErrorCode.BleUnsupported]: 'Bluetooth is not supported on this device',
623
+ [HardwareErrorCode.BleUnavailableWhileUsbConnected]:
624
+ 'Bluetooth is unavailable while USB is connected. Unplug USB and try again.',
615
625
 
616
626
  /**
617
627
  * Runtime Error
@@ -647,7 +657,9 @@ export const HardwareErrorCodeMessage: HardwareErrorCodeMessageMapping = {
647
657
  [HardwareErrorCode.TooManyInputs]: 'Too many inputs',
648
658
  [HardwareErrorCode.PinMismatch]: 'PIN mismatch',
649
659
  [HardwareErrorCode.DeviceLocked]: 'Device locked',
650
- [HardwareErrorCode.KaspaPrevTxIdMismatch]: 'Kaspa previous transaction id mismatch',
660
+ [HardwareErrorCode.WalletSessionInvalid]: 'Wallet session is invalid or expired',
661
+ [HardwareErrorCode.NftStorageLimitReached]:
662
+ 'NFT storage limit reached. Remove an NFT from the device and try again.',
651
663
 
652
664
  /**
653
665
  * Lowlevel transport
@@ -1,9 +1,28 @@
1
- import { isKnownTrezorWebUsbDevice, isOnekeyDevice } from './constants';
1
+ import {
2
+ canonicalizePro2BleAdvertisementName,
3
+ createKnownBleUuidAliases,
4
+ hasOnekeyCommunicationService,
5
+ inferProtocolHintFromUsbId,
6
+ isKnownTrezorWebUsbDevice,
7
+ isOnekeyBluetoothDevice,
8
+ isOnekeyDevice,
9
+ isPro2FindMyAdvertisementName,
10
+ isSameOnekeyBleName,
11
+ matchesKnownBleUuid,
12
+ normalizePro2FindMyAdvertisementName,
13
+ resolveOneKeyUsbDevicePath,
14
+ } from './constants';
2
15
 
3
16
  describe('hardware device identity filters', () => {
4
17
  it('accepts known OneKey BLE names', () => {
5
18
  expect(isOnekeyDevice('Touch A1B2')).toBe(true);
6
19
  expect(isOnekeyDevice('Pro A1B2')).toBe(true);
20
+ expect(isOnekeyDevice('Neo A1B2')).toBe(true);
21
+ expect(isOnekeyDevice('Pro2A1B2')).toBe(true);
22
+ expect(isOnekeyDevice('Pro 2 A1B2')).toBe(true);
23
+ expect(isOnekeyDevice('OneKeyPro2A1B2')).toBe(true);
24
+ expect(isOnekeyDevice('OneKey Pro 2 A1B2')).toBe(true);
25
+ expect(isOnekeyDevice('Neo22D8')).toBe(true);
7
26
  expect(isOnekeyDevice('K1234')).toBe(true);
8
27
  expect(isOnekeyDevice('S8')).toBe(true);
9
28
  });
@@ -13,6 +32,180 @@ describe('hardware device identity filters', () => {
13
32
  expect(isOnekeyDevice('Ledger Nano X')).toBe(false);
14
33
  });
15
34
 
35
+ it('does not identify an FFFD-only advertisement as OneKey', () => {
36
+ expect(isOnekeyBluetoothDevice({ name: 'Find My', serviceUuids: ['fffd'] })).toBe(false);
37
+ expect(
38
+ isOnekeyBluetoothDevice({ serviceUuids: ['0000fffd-0000-1000-8000-00805f9b34fb'] })
39
+ ).toBe(false);
40
+ });
41
+
42
+ it('identifies Pro2 communication advertisements independently of their display name', () => {
43
+ expect(
44
+ isOnekeyBluetoothDevice({
45
+ name: 'Pro2 A1B2',
46
+ serviceUuids: ['0001', 'fffd'],
47
+ })
48
+ ).toBe(true);
49
+ expect(
50
+ isOnekeyBluetoothDevice({
51
+ name: 'Pro 2 A1B2',
52
+ serviceUuids: ['0001', 'fffd'],
53
+ })
54
+ ).toBe(true);
55
+ expect(
56
+ isOnekeyBluetoothDevice({
57
+ localName: 'Pro2 A1B2 - Find My',
58
+ serviceUuids: ['0001', 'fffd'],
59
+ })
60
+ ).toBe(true);
61
+ expect(
62
+ isOnekeyBluetoothDevice({
63
+ name: 'Pro2 5E9D - Finde My',
64
+ serviceUuids: ['0001', 'fffd'],
65
+ })
66
+ ).toBe(true);
67
+ });
68
+
69
+ it('rejects Pro2 Find My entries when the communication service is absent', () => {
70
+ expect(
71
+ isOnekeyBluetoothDevice({
72
+ name: 'Pro2 A1B2 - Find My',
73
+ })
74
+ ).toBe(false);
75
+ expect(
76
+ isOnekeyBluetoothDevice({
77
+ localName: 'OneKey Pro 2 A1B2 - Find My',
78
+ serviceUuids: [],
79
+ })
80
+ ).toBe(false);
81
+ expect(
82
+ isOnekeyBluetoothDevice({
83
+ name: 'Pro2 5E9D - Finde My',
84
+ serviceUuids: ['fffd'],
85
+ })
86
+ ).toBe(false);
87
+ expect(isOnekeyBluetoothDevice({ name: 'Pro2 - Find My' })).toBe(false);
88
+ expect(isOnekeyBluetoothDevice({ name: 'Pro2 G12Z - Find My' })).toBe(false);
89
+ });
90
+
91
+ it('uses a broad Find My guard without broadening public name normalization', () => {
92
+ expect(isPro2FindMyAdvertisementName('Pro2 Griffin - Find My')).toBe(true);
93
+ expect(isPro2FindMyAdvertisementName('Pro2 Griffin')).toBe(false);
94
+ expect(isPro2FindMyAdvertisementName('Pro2 22D8 - Find My')).toBe(true);
95
+ expect(normalizePro2FindMyAdvertisementName('Pro2 Griffin - Find My')).toBe(
96
+ 'Pro2 Griffin - Find My'
97
+ );
98
+ expect(normalizePro2FindMyAdvertisementName('Pro2 22D8 - Find My')).toBe('Pro2 22D8');
99
+ });
100
+
101
+ it.each([
102
+ ['Pro2 A1B2 - Find My', 'Pro2 A1B2'],
103
+ ['Pro2 5E9D - Finde My', 'Pro2 5E9D'],
104
+ [' OneKey Pro 2 A1B2 Find My ', ' OneKey Pro 2 A1B2'],
105
+ ['OneKey Pro-2 22D8--Find-My', 'OneKey Pro-2 22D8'],
106
+ ['Pro2 22D8FindMy', 'Pro2 22D8'],
107
+ ['Pro2 22D8 - Fin', 'Pro2 22D8'],
108
+ ['Pro2 22D8FindM', 'Pro2 22D8'],
109
+ [`Pro2 22D8${'\t'.repeat(10_000)}Find My`, 'Pro2 22D8'],
110
+ ['Pro2 A1B2', 'Pro2 A1B2'],
111
+ ['Pro2 Griffin', 'Pro2 Griffin'],
112
+ ['Find My', 'Find My'],
113
+ ])('normalizes the public Pro2 advertisement name %s', (name, expected) => {
114
+ expect(normalizePro2FindMyAdvertisementName(name)).toBe(expected);
115
+ });
116
+
117
+ it.each([
118
+ ['Pro2 A1B2', 'Pro 2 A1B2'],
119
+ ['Pro2A1B2', 'Pro 2 A1B2'],
120
+ ['Pro 2 A1B2', 'Pro 2 A1B2'],
121
+ ['Pro 2 0088', 'Pro 2 0088'],
122
+ ['Pro2 22D8 - Find My', 'Pro 2 22D8'],
123
+ ['Pro 2 A1B2 - Find My', 'Pro 2 A1B2'],
124
+ ['OneKeyPro2A1B2', 'OneKey Pro 2 A1B2'],
125
+ ['OneKey Pro 2 A1B2', 'OneKey Pro 2 A1B2'],
126
+ ['OneKey Pro 2', 'OneKey Pro 2'],
127
+ ['Pro A1B2', 'Pro A1B2'],
128
+ ['Pro 22D8', 'Pro 22D8'],
129
+ ['Pro 2D8F', 'Pro 2D8F'],
130
+ ['OneKey Pro 22D8', 'OneKey Pro 22D8'],
131
+ ['Neo 22D8', 'Neo 22D8'],
132
+ ])('canonicalizes the public Pro2 advertisement name %s', (name, expected) => {
133
+ expect(canonicalizePro2BleAdvertisementName(name)).toBe(expected);
134
+ });
135
+
136
+ it('treats compact and spaced Pro2 BLE names as the same device', () => {
137
+ expect(isSameOnekeyBleName('Pro2 6136', 'Pro 2 6136')).toBe(true);
138
+ expect(isSameOnekeyBleName('Pro2 6136 - Find My', 'Pro 2 6136')).toBe(true);
139
+ expect(isSameOnekeyBleName('Pro 2 6136', 'Pro 2 0088')).toBe(false);
140
+ expect(isSameOnekeyBleName('Pro A1B2', 'Pro 2 A1B2')).toBe(false);
141
+ expect(isSameOnekeyBleName('Pro 22D8', 'Pro 22D8')).toBe(true);
142
+ expect(isSameOnekeyBleName('Pro 22D8', 'Pro2 22D8')).toBe(false);
143
+ expect(isSameOnekeyBleName('Pro 22D8', 'Pro 2 22D8')).toBe(false);
144
+ });
145
+
146
+ it('keeps OneKey discovery on the communication service', () => {
147
+ expect(
148
+ isOnekeyBluetoothDevice({
149
+ name: 'OneKey Pro 2',
150
+ serviceUuids: ['00000001-0000-1000-8000-00805f9b34fb'],
151
+ })
152
+ ).toBe(true);
153
+ });
154
+
155
+ it('only aliases standard Bluetooth Base UUID representations', () => {
156
+ const aliases = createKnownBleUuidAliases('00000001-0000-1000-8000-00805f9b34fb');
157
+
158
+ expect(matchesKnownBleUuid('0001', aliases)).toBe(true);
159
+ expect(matchesKnownBleUuid('00000001', aliases)).toBe(true);
160
+ expect(matchesKnownBleUuid('abcd0001-1234-5678-9012-abcdefabcdef', aliases)).toBe(false);
161
+ });
162
+
163
+ it('does not treat vendor-specific UUID fragments as communication or FIDO services', () => {
164
+ expect(
165
+ isOnekeyBluetoothDevice({
166
+ name: 'OneKey Pro 2',
167
+ serviceUuids: ['abcdfffd-1234-5678-9012-abcdefabcdef'],
168
+ })
169
+ ).toBe(true);
170
+ expect(
171
+ isOnekeyBluetoothDevice({
172
+ serviceUuids: ['abcd0001-1234-5678-9012-abcdefabcdef'],
173
+ })
174
+ ).toBe(false);
175
+ });
176
+
177
+ it('requires an explicitly advertised communication service for strict discovery', () => {
178
+ expect(hasOnekeyCommunicationService([])).toBe(false);
179
+ expect(hasOnekeyCommunicationService(['0001'])).toBe(true);
180
+ expect(hasOnekeyCommunicationService(['abcd0001-1234-5678-9012-abcdefabcdef'])).toBe(false);
181
+ });
182
+
183
+ it('hints Protocol V2 from the current Pro2/Neo USB ID only', () => {
184
+ expect(inferProtocolHintFromUsbId(0x1209, 0x4f4c)).toBe('V2');
185
+ expect(inferProtocolHintFromUsbId(0x1209, 0x53c1)).toBeUndefined();
186
+ expect(inferProtocolHintFromUsbId(0x1209, 0x4f4a)).toBeUndefined();
187
+ expect(inferProtocolHintFromUsbId(0x1209, 0x4f4b)).toBeUndefined();
188
+ });
189
+
190
+ it('uses the USB serial when present and synthesizes a path when firmware omits it', () => {
191
+ expect(
192
+ resolveOneKeyUsbDevicePath({
193
+ vendorId: 0x1209,
194
+ productId: 0x4f4c,
195
+ productName: 'OneKey Pro 2',
196
+ serialNumber: 'PRO2-SERIAL',
197
+ })
198
+ ).toBe('PRO2-SERIAL');
199
+ expect(
200
+ resolveOneKeyUsbDevicePath({
201
+ vendorId: 0x1209,
202
+ productId: 0x4f4c,
203
+ productName: 'OneKey Neo',
204
+ serialNumber: '',
205
+ })
206
+ ).toBe('usb-1209-4f4c-onekey-neo');
207
+ });
208
+
16
209
  it('only filters WebUSB descriptors that are explicitly identified as Trezor', () => {
17
210
  expect(
18
211
  isKnownTrezorWebUsbDevice({