@onekeyfe/hd-core 1.2.0-alpha.164 → 1.2.0-alpha.166

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.
Files changed (40) hide show
  1. package/__tests__/device-state-events.test.ts +2 -2
  2. package/__tests__/device-state-mapper.test.ts +11 -2
  3. package/__tests__/device-utils.test.ts +6 -0
  4. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +13 -3
  5. package/__tests__/protocol-v2-resources.test.ts +35 -2
  6. package/__tests__/protocol-v2.test.ts +89 -42
  7. package/__tests__/search-devices.test.ts +4 -3
  8. package/dist/api/FirmwareUpdateV4.d.ts +3 -1
  9. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  10. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +0 -15
  11. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  12. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  13. package/dist/core/index.d.ts.map +1 -1
  14. package/dist/device/Device.d.ts.map +1 -1
  15. package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
  16. package/dist/index.d.ts +2 -22
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +98 -245
  19. package/dist/protocols/protocol-v2/resources.d.ts +1 -0
  20. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  21. package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
  22. package/dist/utils/deviceInfoUtils.d.ts.map +1 -1
  23. package/package.json +4 -4
  24. package/src/api/FirmwareUpdateV4.ts +75 -179
  25. package/src/api/SearchDevices.ts +2 -2
  26. package/src/api/firmware/FirmwareUpdatePlan.ts +0 -41
  27. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +10 -6
  28. package/src/core/index.ts +16 -6
  29. package/src/device/Device.ts +3 -1
  30. package/src/device/DeviceStateMapper.ts +2 -2
  31. package/src/deviceProfile/buildDeviceFeatures.ts +7 -2
  32. package/src/index.ts +0 -6
  33. package/src/protocols/protocol-v2/resources.ts +16 -12
  34. package/src/types/api/firmwareUpdate.ts +1 -1
  35. package/src/utils/deviceFeaturesCompat.ts +9 -4
  36. package/src/utils/deviceInfoUtils.ts +3 -1
  37. package/__tests__/firmware-memory-host.test.ts +0 -126
  38. package/dist/api/firmware/FirmwareMemoryHost.d.ts +0 -22
  39. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +0 -1
  40. package/src/api/firmware/FirmwareMemoryHost.ts +0 -143
@@ -49,10 +49,22 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
49
49
  };
50
50
  }
51
51
 
52
- const PROTOCOL_V2_RESOURCE_DEVICE_ROOTS = ['vol0:/bundles/', 'vol0:/loaders/rom/'] as const;
52
+ export function isProtocolV2ResourceArchiveEntryName(entryName: string): boolean {
53
+ const normalized = entryName.replace(/\\/g, '/');
54
+ if (!normalized.toLowerCase().endsWith('.okpkg')) {
55
+ return false;
56
+ }
57
+ const parts = normalized.split('/');
58
+ const fileName = parts[parts.length - 1] ?? '';
59
+ return (
60
+ fileName.length > 0 &&
61
+ !fileName.startsWith('.') &&
62
+ !parts.some(part => part === '__MACOSX' || part === '.' || part === '..' || part === '')
63
+ );
64
+ }
53
65
 
54
- function isAllowedResourceDevicePath(path: string): boolean {
55
- if (
66
+ function isSafeResourceDevicePath(path: string): boolean {
67
+ return !(
56
68
  path.includes('\\') ||
57
69
  path.includes('//') ||
58
70
  [...path].some(char => {
@@ -60,14 +72,6 @@ function isAllowedResourceDevicePath(path: string): boolean {
60
72
  return code <= 0x1f || code === 0x7f;
61
73
  }) ||
62
74
  path.split('/').some(part => part === '.' || part === '..')
63
- ) {
64
- return false;
65
- }
66
- if (path === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH) {
67
- return true;
68
- }
69
- return (
70
- path.endsWith('.okpkg') && PROTOCOL_V2_RESOURCE_DEVICE_ROOTS.some(root => path.startsWith(root))
71
75
  );
72
76
  }
73
77
 
@@ -93,7 +97,7 @@ function readResourceDevicePath(bytes: Uint8Array): string {
93
97
  throw new Error('Invalid Pro2 RESOURCE package device path metadata');
94
98
  }
95
99
  const path = readAscii(pathBytes, 0, pathBytes.byteLength);
96
- if (!isAllowedResourceDevicePath(path)) {
100
+ if (!isSafeResourceDevicePath(path)) {
97
101
  throw new Error(`Invalid Pro2 RESOURCE package device path: ${path}`);
98
102
  }
99
103
  return path;
@@ -158,7 +158,7 @@ export interface FirmwareUpdateV4Params {
158
158
  se02Binary?: ArrayBuffer;
159
159
  se03Binary?: ArrayBuffer;
160
160
  se04Binary?: ArrayBuffer;
161
- /** Complete Protocol V2 resource ZIP for local development; Core converts it to a local PreparedPlan. */
161
+ /** Complete Protocol V2 resource ZIP. Core parses RESC packages and writes only changed files. */
162
162
  resourceArchiveBinary?: ArrayBuffer;
163
163
  forcedUpdateRes?: boolean;
164
164
  artifactReader?: FirmwareArtifactReader;
@@ -1,4 +1,8 @@
1
- import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
1
+ import {
2
+ EDeviceType,
3
+ EFirmwareType,
4
+ canonicalizePro2BleAdvertisementName,
5
+ } from '@onekeyfe/hd-shared';
2
6
  import { Enum_Capability } from '@onekeyfe/hd-transport';
3
7
 
4
8
  import type { PROTO } from '../constants';
@@ -125,9 +129,10 @@ export const resolveDeviceFirmwareType = (features?: DeviceFeaturesInput): EFirm
125
129
  export const resolveDeviceBleName = (features?: DeviceFeaturesInput): string | null => {
126
130
  if (!features) return null;
127
131
  const compatible = asCompatibleFeatures(features);
128
- return (
129
- firstNonEmptyString(compatible.bleName, compatible.onekey_ble_name, compatible.ble_name) ?? null
130
- );
132
+ const bleName =
133
+ firstNonEmptyString(compatible.bleName, compatible.onekey_ble_name, compatible.ble_name) ??
134
+ null;
135
+ return bleName ? canonicalizePro2BleAdvertisementName(bleName) : null;
131
136
  };
132
137
 
133
138
  export const resolveDeviceFirmwareVersion = (features?: DeviceFeaturesInput): string | null => {
@@ -31,7 +31,9 @@ export const getDeviceTypeByBleName = (name?: string): IDeviceType => {
31
31
  if (/^Touch/i.test(name)) return EDeviceType.Touch;
32
32
 
33
33
  const compactName = name.replace(/[\s-]/g, '');
34
- if (/\bPro\s*2\b/i.test(name) || /^Pro2/i.test(name) || /^(?:OneKey)?Pro2/i.test(compactName)) {
34
+ // Require a 4-hex Pro2 suffix in the compact form. A bare `^Pro2` prefix
35
+ // would also match OneKey Pro names such as "Pro 22D8" / "Pro 2D8F".
36
+ if (/\bPro\s*2\b/i.test(name) || /^(?:OneKey)?Pro2[a-f0-9]{4}$/i.test(compactName)) {
35
37
  return EDeviceType.Pro2;
36
38
  }
37
39
  if (/\bNeo\b/i.test(name) || /^Neo/i.test(name) || /^(?:OneKey)?Neo/i.test(compactName)) {
@@ -1,126 +0,0 @@
1
- import { prepareFirmwareUpdateV4MemoryHost } from '../src/api/firmware/FirmwareMemoryHost';
2
-
3
- import type { CoreApi } from '../src/types/api';
4
- import type { FirmwareUpdatePlan } from '../src/types/api/firmwareUpdatePlan';
5
-
6
- describe('prepareFirmwareUpdateV4MemoryHost', () => {
7
- test('registers artifact-backed component and resource readers', async () => {
8
- const componentBinary = new Uint8Array([1, 2, 3, 4]).buffer;
9
- const archiveBinary = new Uint8Array([5, 6]).buffer;
10
- const manifestBinary = new TextEncoder().encode('{"schema":1}').buffer;
11
- let artifactReader: Parameters<
12
- CoreApi['registerFirmwareUpdateHostBinding']
13
- >[0]['artifactReader'];
14
- const unregisterFirmwareUpdateHostBinding = jest.fn();
15
- const sdk = {
16
- prepareFirmwareUpdatePlan: jest.fn(({ plan, artifacts }) => ({
17
- schemaVersion: 2,
18
- preparedPlanDigest: 'b'.repeat(64),
19
- planDigest: plan.planDigest,
20
- networkPolicy: 'forbid',
21
- executor: plan.executor,
22
- deviceIdentity: plan.deviceIdentity,
23
- deviceModel: plan.deviceModel,
24
- firmwareType: plan.firmwareType,
25
- platform: plan.platform,
26
- leaseRef: 'test-lease',
27
- targetsToUpdate: plan.targetsToUpdate,
28
- artifacts: plan.artifacts.map(planArtifact => {
29
- const input = artifacts.find(item => item.artifactId === planArtifact.artifactId);
30
- return {
31
- ...planArtifact,
32
- artifact: input?.artifact,
33
- materializedEntries: input?.materializedEntries,
34
- };
35
- }),
36
- })),
37
- registerFirmwareUpdateHostBinding: jest.fn(binding => {
38
- artifactReader = binding.artifactReader;
39
- return 7;
40
- }),
41
- unregisterFirmwareUpdateHostBinding,
42
- } as unknown as Pick<
43
- CoreApi,
44
- | 'prepareFirmwareUpdatePlan'
45
- | 'registerFirmwareUpdateHostBinding'
46
- | 'unregisterFirmwareUpdateHostBinding'
47
- >;
48
- const plan = {
49
- schemaVersion: 2,
50
- planDigest: 'a'.repeat(64),
51
- executor: 'v4',
52
- deviceIdentity: 'device-id',
53
- deviceModel: 'pro2',
54
- firmwareType: 0,
55
- platform: 'web',
56
- targetsToUpdate: ['boot', 'resource'],
57
- artifacts: [
58
- {
59
- artifactId: 'component:boot',
60
- role: 'component',
61
- target: 'boot',
62
- url: 'https://example.com/boot.okpkg',
63
- container: 'raw',
64
- },
65
- {
66
- artifactId: 'resource:archive',
67
- role: 'resourceBundle',
68
- target: 'resource',
69
- url: 'https://example.com/resource.zip',
70
- container: 'zip',
71
- logicalName: 'resource-archive',
72
- },
73
- ],
74
- } as FirmwareUpdatePlan;
75
-
76
- const host = prepareFirmwareUpdateV4MemoryHost({
77
- sdk,
78
- plan,
79
- artifacts: [
80
- { artifactId: 'component:boot', binary: componentBinary },
81
- {
82
- artifactId: 'resource:archive',
83
- binary: archiveBinary,
84
- materializedEntries: [{ entryName: 'manifest.json', binary: manifestBinary }],
85
- },
86
- ],
87
- });
88
-
89
- expect(host.hostBindingGeneration).toBe(7);
90
- expect(sdk.registerFirmwareUpdateHostBinding).toHaveBeenCalledWith(
91
- expect.objectContaining({ preparedPlanDigest: host.preparedPlan.preparedPlanDigest })
92
- );
93
- const componentArtifact = host.preparedPlan.artifacts.find(
94
- artifact => artifact.target === 'boot'
95
- )?.artifact;
96
- const resourceEntryArtifact = host.preparedPlan.artifacts.find(
97
- artifact => artifact.target === 'resource'
98
- )?.materializedEntries?.[0]?.artifact;
99
- expect(componentArtifact?.size).toBe(componentBinary.byteLength);
100
- expect(resourceEntryArtifact?.size).toBe(manifestBinary.byteLength);
101
- if (!artifactReader || !componentArtifact || !resourceEntryArtifact) {
102
- throw new Error('Firmware memory host test setup is incomplete');
103
- }
104
- new Uint8Array(componentBinary).fill(9);
105
- const opened = await artifactReader.open({
106
- artifactRef: componentArtifact.artifactRef,
107
- });
108
- const chunk = await artifactReader.read({
109
- readerId: opened.readerId,
110
- offset: 1,
111
- length: 2,
112
- });
113
- expect(Array.from(new Uint8Array(chunk.data))).toEqual([2, 3]);
114
- await artifactReader.close({ readerId: opened.readerId });
115
-
116
- // Materialized ZIP entries are receipts only. Execution opens the approved
117
- // archive and re-derives these bytes, so the memory host must not retain a
118
- // second readable copy of every expanded resource file.
119
- expect(() => artifactReader.open({ artifactRef: resourceEntryArtifact.artifactRef })).toThrow(
120
- 'Firmware memory artifact is unavailable'
121
- );
122
-
123
- host.release();
124
- expect(unregisterFirmwareUpdateHostBinding).toHaveBeenCalledWith(7);
125
- });
126
- });
@@ -1,22 +0,0 @@
1
- import type { CoreApi } from '../../types/api';
2
- import type { FirmwareUpdatePlan } from '../../types/api/firmwareUpdatePlan';
3
- export type FirmwareMemoryArtifactEntry = {
4
- entryName: string;
5
- binary: ArrayBuffer;
6
- };
7
- export type FirmwareMemoryArtifact = {
8
- artifactId: string;
9
- binary: ArrayBuffer;
10
- materializedEntries?: FirmwareMemoryArtifactEntry[];
11
- };
12
- export type FirmwareUpdateV4MemoryHost = {
13
- preparedPlan: ReturnType<CoreApi['prepareFirmwareUpdatePlan']>;
14
- hostBindingGeneration: number;
15
- release: () => void;
16
- };
17
- export declare function prepareFirmwareUpdateV4MemoryHost({ sdk, plan, artifacts, }: {
18
- sdk: Pick<CoreApi, 'prepareFirmwareUpdatePlan' | 'registerFirmwareUpdateHostBinding' | 'unregisterFirmwareUpdateHostBinding'>;
19
- plan: FirmwareUpdatePlan;
20
- artifacts: FirmwareMemoryArtifact[];
21
- }): FirmwareUpdateV4MemoryHost;
22
- //# sourceMappingURL=FirmwareMemoryHost.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FirmwareMemoryHost.d.ts","sourceRoot":"","sources":["../../../src/api/firmware/FirmwareMemoryHost.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAK/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAE7E,MAAM,MAAM,2BAA2B,GAAG;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;IACpB,mBAAmB,CAAC,EAAE,2BAA2B,EAAE,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC,CAAC;IAC/D,qBAAqB,EAAE,MAAM,CAAC;IAC9B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAaF,wBAAgB,iCAAiC,CAAC,EAChD,GAAG,EACH,IAAI,EACJ,SAAS,GACV,EAAE;IACD,GAAG,EAAE,IAAI,CACP,OAAO,EACL,2BAA2B,GAC3B,mCAAmC,GACnC,qCAAqC,CACxC,CAAC;IACF,IAAI,EAAE,kBAAkB,CAAC;IACzB,SAAS,EAAE,sBAAsB,EAAE,CAAC;CACrC,GAAG,0BAA0B,CA0F7B"}
@@ -1,143 +0,0 @@
1
- import { sha256 } from '@noble/hashes/sha256';
2
- import { bytesToHex } from '@noble/hashes/utils';
3
- import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
4
-
5
- import type { CoreApi } from '../../types/api';
6
- import type {
7
- FirmwareArtifactReader,
8
- FirmwareArtifactReference,
9
- } from '../../types/api/firmwareUpdate';
10
- import type { FirmwareUpdatePlan } from '../../types/api/firmwareUpdatePlan';
11
-
12
- export type FirmwareMemoryArtifactEntry = {
13
- entryName: string;
14
- binary: ArrayBuffer;
15
- };
16
-
17
- export type FirmwareMemoryArtifact = {
18
- artifactId: string;
19
- binary: ArrayBuffer;
20
- materializedEntries?: FirmwareMemoryArtifactEntry[];
21
- };
22
-
23
- export type FirmwareUpdateV4MemoryHost = {
24
- preparedPlan: ReturnType<CoreApi['prepareFirmwareUpdatePlan']>;
25
- hostBindingGeneration: number;
26
- release: () => void;
27
- };
28
-
29
- let memoryHostSequence = 0;
30
-
31
- const createReference = (binary: ArrayBuffer, prefix: string): FirmwareArtifactReference => {
32
- const digest = bytesToHex(sha256(new Uint8Array(binary)));
33
- return {
34
- artifactRef: `fwmem:${prefix}:${digest.slice(0, 32)}`,
35
- size: binary.byteLength,
36
- sha256: digest,
37
- };
38
- };
39
-
40
- export function prepareFirmwareUpdateV4MemoryHost({
41
- sdk,
42
- plan,
43
- artifacts,
44
- }: {
45
- sdk: Pick<
46
- CoreApi,
47
- | 'prepareFirmwareUpdatePlan'
48
- | 'registerFirmwareUpdateHostBinding'
49
- | 'unregisterFirmwareUpdateHostBinding'
50
- >;
51
- plan: FirmwareUpdatePlan;
52
- artifacts: FirmwareMemoryArtifact[];
53
- }): FirmwareUpdateV4MemoryHost {
54
- if (plan.executor !== 'v4') {
55
- throw ERRORS.TypedError(
56
- HardwareErrorCode.RuntimeError,
57
- 'Firmware memory host only supports V4 plans'
58
- );
59
- }
60
- memoryHostSequence += 1;
61
- const hostId = `${Date.now()}:${memoryHostSequence}`;
62
- const binaries = new Map<string, Uint8Array>();
63
- const inputs = artifacts.map((input, artifactIndex) => {
64
- const artifactBinary = new Uint8Array(input.binary).slice();
65
- const artifact = createReference(
66
- artifactBinary.buffer as ArrayBuffer,
67
- `${hostId}:artifact:${artifactIndex}`
68
- );
69
- binaries.set(artifact.artifactRef, artifactBinary);
70
- const materializedEntries = input.materializedEntries?.map((entry, entryIndex) => {
71
- const entryArtifact = createReference(
72
- entry.binary,
73
- `${hostId}:entry:${artifactIndex}:${entryIndex}`
74
- );
75
- // Entry references are compact receipts for bytes that will be re-derived
76
- // from the verified archive. Retaining another readable copy here doubles
77
- // resource memory without adding an execution trust boundary.
78
- return {
79
- entryName: entry.entryName,
80
- artifact: entryArtifact,
81
- };
82
- });
83
- return {
84
- artifactId: input.artifactId,
85
- artifact,
86
- ...(materializedEntries?.length ? { materializedEntries } : {}),
87
- };
88
- });
89
- const preparedPlan = sdk.prepareFirmwareUpdatePlan({
90
- plan,
91
- leaseRef: `fwmemlease:${hostId}`,
92
- artifacts: inputs,
93
- });
94
- const readers = new Map<string, Uint8Array>();
95
- let readerSequence = 0;
96
- const artifactReader: FirmwareArtifactReader = {
97
- open({ artifactRef }) {
98
- const binary = binaries.get(artifactRef);
99
- if (!binary) {
100
- throw ERRORS.TypedError(
101
- HardwareErrorCode.RuntimeError,
102
- 'Firmware memory artifact is unavailable'
103
- );
104
- }
105
- readerSequence += 1;
106
- const readerId = `fwmemreader:${hostId}:${readerSequence}`;
107
- readers.set(readerId, binary);
108
- return Promise.resolve({ readerId, size: binary.byteLength });
109
- },
110
- read({ readerId, offset, length }) {
111
- const binary = readers.get(readerId);
112
- if (!binary || offset < 0 || length <= 0 || offset + length > binary.byteLength) {
113
- throw ERRORS.TypedError(
114
- HardwareErrorCode.RuntimeError,
115
- 'Firmware memory artifact read is invalid'
116
- );
117
- }
118
- const data = binary.slice(offset, offset + length).buffer;
119
- return Promise.resolve({
120
- data,
121
- bytesRead: data.byteLength,
122
- eof: offset + length === binary.byteLength,
123
- });
124
- },
125
- close({ readerId }) {
126
- readers.delete(readerId);
127
- return Promise.resolve();
128
- },
129
- };
130
- const hostBindingGeneration = sdk.registerFirmwareUpdateHostBinding({
131
- artifactReader,
132
- preparedPlanDigest: preparedPlan.preparedPlanDigest,
133
- });
134
- return {
135
- preparedPlan,
136
- hostBindingGeneration,
137
- release: () => {
138
- sdk.unregisterFirmwareUpdateHostBinding(hostBindingGeneration);
139
- readers.clear();
140
- binaries.clear();
141
- },
142
- };
143
- }