@onekeyfe/hd-core 1.2.0-alpha.142 → 1.2.0-alpha.144

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,29 +1,20 @@
1
- import type {
2
- IProtocolV2ResourceManifest,
3
- IProtocolV2ResourceManifestFile,
4
- IProtocolV2Resources,
5
- } from '../../types';
6
- import type { FirmwareUpdateV4Target } from '../../types/api/firmwareUpdate';
1
+ import { bytesToHex } from '@noble/hashes/utils';
2
+
3
+ import type { IProtocolV2Resources, IVersionArray } from '../../types';
7
4
 
8
5
  export const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH =
9
6
  'vol0:/loaders/bootloader/boot_resource.okpkg';
10
7
  export const PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH = `${PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH}.staging`;
11
8
  export const PROTOCOL_V2_ROM_PARAMS_PACKAGE_PATH = 'vol0:/loaders/rom/params.okpkg';
9
+ export const PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_SIZE = 0x5f90;
12
10
 
13
- const SHA256_HEX_LENGTH = 64;
14
-
15
- function normalizeHex(value: unknown, expectedLength: number, field: string): string {
16
- if (typeof value !== 'string') {
17
- throw new Error(`Invalid Pro2 resource ${field}: expected a hexadecimal string`);
18
- }
19
- const normalized = value.replace(/^0x/i, '').toLowerCase();
20
- if (normalized.length !== expectedLength || !/^[0-9a-f]+$/.test(normalized)) {
21
- throw new Error(
22
- `Invalid Pro2 resource ${field}: expected ${expectedLength} hexadecimal characters`
23
- );
24
- }
25
- return normalized;
26
- }
11
+ const PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_VERSION = 1;
12
+ const PROTOCOL_V2_RESOURCE_PACKAGE_FLEXIBLE_OFFSET = 0x6c;
13
+ const PROTOCOL_V2_RESOURCE_PACKAGE_FLEXIBLE_SIZE = 64;
14
+ const PROTOCOL_V2_RESOURCE_PACKAGE_PAYLOAD_HASH_OFFSET = 0x200;
15
+ const PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_HASH_OFFSET = 0x240;
16
+ const PROTOCOL_V2_RESOURCE_PACKAGE_HASH_SIZE = 64;
17
+ const PROTOCOL_V2_RESOURCE_PACKAGE_TYPE = 'RESC';
27
18
 
28
19
  /** Validate a complete Pro2 stable resource set from remote configuration. */
29
20
  export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources | undefined {
@@ -58,114 +49,113 @@ export function parseProtocolV2Resources(value: unknown): IProtocolV2Resources |
58
49
  };
59
50
  }
60
51
 
61
- const PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS = [
62
- 'vol0:/bundles/',
63
- 'vol0:/loaders/rom/',
64
- ] as const;
52
+ const PROTOCOL_V2_RESOURCE_DEVICE_ROOTS = ['vol0:/bundles/', 'vol0:/loaders/rom/'] as const;
65
53
 
66
- function isAllowedManifestDevicePath(path: string): boolean {
54
+ function isAllowedResourceDevicePath(path: string): boolean {
67
55
  if (
68
- !path.endsWith('.okpkg') ||
69
56
  path.includes('\\') ||
70
57
  path.includes('//') ||
58
+ [...path].some(char => {
59
+ const code = char.charCodeAt(0);
60
+ return code <= 0x1f || code === 0x7f;
61
+ }) ||
71
62
  path.split('/').some(part => part === '.' || part === '..')
72
63
  ) {
73
64
  return false;
74
65
  }
75
- if (path === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_PATH) {
66
+ if (path === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH) {
76
67
  return true;
77
68
  }
78
- return PROTOCOL_V2_RESOURCE_MANIFEST_DEVICE_ROOTS.some(root => path.startsWith(root));
69
+ return (
70
+ path.endsWith('.okpkg') && PROTOCOL_V2_RESOURCE_DEVICE_ROOTS.some(root => path.startsWith(root))
71
+ );
79
72
  }
80
73
 
81
- function assertManifestString(value: unknown, field: string): string {
82
- if (typeof value !== 'string' || value.length === 0) {
83
- throw new Error(`Invalid Pro2 resource manifest ${field}`);
84
- }
85
- return value;
74
+ function readAscii(bytes: Uint8Array, offset: number, length: number): string {
75
+ return Array.from(bytes.slice(offset, offset + length))
76
+ .map(byte => String.fromCharCode(byte))
77
+ .join('');
86
78
  }
87
79
 
88
- function assertManifestRelativePath(value: unknown, field: string): string {
89
- const path = assertManifestString(value, field);
80
+ function readResourceDevicePath(bytes: Uint8Array): string {
81
+ const metadata = bytes.slice(
82
+ PROTOCOL_V2_RESOURCE_PACKAGE_FLEXIBLE_OFFSET,
83
+ PROTOCOL_V2_RESOURCE_PACKAGE_FLEXIBLE_OFFSET + PROTOCOL_V2_RESOURCE_PACKAGE_FLEXIBLE_SIZE
84
+ );
85
+ const terminator = metadata.indexOf(0);
86
+ const pathBytes = terminator === -1 ? metadata : metadata.slice(0, terminator);
87
+ const padding = terminator === -1 ? new Uint8Array(0) : metadata.slice(terminator);
90
88
  if (
91
- path.startsWith('/') ||
92
- path.includes('\\') ||
93
- path.includes(':') ||
94
- path.split('/').some(part => !part || part === '.' || part === '..')
89
+ pathBytes.byteLength === 0 ||
90
+ Array.from(pathBytes).some(byte => byte < 0x20 || byte > 0x7e) ||
91
+ Array.from(padding).some(byte => byte !== 0)
95
92
  ) {
96
- throw new Error(`Invalid Pro2 resource manifest ${field}`);
93
+ throw new Error('Invalid Pro2 RESOURCE package device path metadata');
94
+ }
95
+ const path = readAscii(pathBytes, 0, pathBytes.byteLength);
96
+ if (!isAllowedResourceDevicePath(path)) {
97
+ throw new Error(`Invalid Pro2 RESOURCE package device path: ${path}`);
97
98
  }
98
99
  return path;
99
100
  }
100
101
 
101
- function parseProtocolV2ResourceManifestFile(
102
- value: unknown,
103
- index: number
104
- ): IProtocolV2ResourceManifestFile {
105
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
106
- throw new Error(`Invalid Pro2 resource manifest files[${index}]`);
107
- }
108
- const file = value as Partial<IProtocolV2ResourceManifestFile>;
109
- const archivePath = assertManifestRelativePath(file.archive_path, `files[${index}].archive_path`);
110
- const originalName =
111
- file.original_name === undefined
112
- ? archivePath.split('/').pop() ?? archivePath
113
- : assertManifestRelativePath(file.original_name, `files[${index}].original_name`);
114
- if (originalName.includes('/')) {
115
- throw new Error(`Invalid Pro2 resource manifest files[${index}].original_name`);
116
- }
117
- const devicePath = assertManifestString(file.device_path, `files[${index}].device_path`);
118
- if (!isAllowedManifestDevicePath(devicePath)) {
119
- throw new Error(`Invalid Pro2 resource manifest files[${index}].device_path`);
120
- }
121
- if (!Number.isSafeInteger(file.size) || Number(file.size) <= 0) {
122
- throw new Error(`Invalid Pro2 resource manifest files[${index}].size`);
123
- }
124
- const digest = normalizeHex(file.sha256, SHA256_HEX_LENGTH, `files[${index}].sha256`);
125
- if (!archivePath.endsWith('.okpkg') || !originalName.endsWith('.okpkg')) {
126
- throw new Error(`Invalid Pro2 resource manifest files[${index}] package extension`);
127
- }
128
- return {
129
- archive_path: archivePath,
130
- original_name: originalName,
131
- device_path: devicePath,
132
- size: Number(file.size),
133
- sha256: digest,
134
- ...(file.signed === undefined ? {} : { signed: file.signed }),
135
- ...(file.sig_algo === undefined ? {} : { sig_algo: file.sig_algo }),
136
- ...(file.payload_version === undefined ? {} : { payload_version: file.payload_version }),
137
- };
138
- }
102
+ export type ProtocolV2ResourcePackageHeader = {
103
+ version: IVersionArray;
104
+ payloadLength: number;
105
+ devicePath: string;
106
+ payloadHash: string;
107
+ headerHash: string;
108
+ };
139
109
 
140
- export function parseProtocolV2ResourceManifest(value: unknown): IProtocolV2ResourceManifest {
141
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
142
- throw new Error('Invalid Pro2 resource manifest');
143
- }
144
- const manifest = value as Partial<IProtocolV2ResourceManifest>;
145
- if (!Array.isArray(manifest.files)) {
146
- throw new Error('Invalid Pro2 resource manifest files');
147
- }
148
- const files = manifest.files.map(parseProtocolV2ResourceManifestFile);
149
- const devicePaths = new Set(files.map(file => file.device_path));
150
- const archivePaths = new Set(files.map(file => file.archive_path));
110
+ export function parseProtocolV2ResourcePackageHeader(
111
+ bytes: Uint8Array,
112
+ packageSize: number
113
+ ): ProtocolV2ResourcePackageHeader {
114
+ if (bytes.byteLength < PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_SIZE) {
115
+ throw new Error('Pro2 RESOURCE package is shorter than its header');
116
+ }
117
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
118
+ const headerVersion = view.getUint32(0x04, true);
119
+ const headerLength = view.getUint32(0x0c, true);
120
+ const payloadLength = view.getUint32(0x14, true);
151
121
  if (
152
- files.length === 0 ||
153
- devicePaths.size !== files.length ||
154
- archivePaths.size !== files.length
122
+ readAscii(bytes, 0, 4) !== 'OKPP' ||
123
+ readAscii(bytes, 0x08, 4) !== PROTOCOL_V2_RESOURCE_PACKAGE_TYPE ||
124
+ headerVersion !== PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_VERSION ||
125
+ headerLength !== PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_SIZE ||
126
+ payloadLength <= 0 ||
127
+ headerLength + payloadLength !== packageSize
155
128
  ) {
156
- throw new Error('Invalid Pro2 resource manifest file set');
129
+ throw new Error('Invalid Pro2 RESOURCE package header');
157
130
  }
131
+
132
+ const packedVersion = view.getUint32(0x10, true);
158
133
  return {
159
- files,
134
+ version: [
135
+ Math.floor(packedVersion / 0x10000) % 0x100,
136
+ Math.floor(packedVersion / 0x100) % 0x100,
137
+ packedVersion % 0x100,
138
+ ],
139
+ payloadLength,
140
+ devicePath: readResourceDevicePath(bytes),
141
+ payloadHash: bytesToHex(
142
+ bytes.slice(
143
+ PROTOCOL_V2_RESOURCE_PACKAGE_PAYLOAD_HASH_OFFSET,
144
+ PROTOCOL_V2_RESOURCE_PACKAGE_PAYLOAD_HASH_OFFSET + PROTOCOL_V2_RESOURCE_PACKAGE_HASH_SIZE
145
+ )
146
+ ),
147
+ headerHash: bytesToHex(
148
+ bytes.slice(
149
+ PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_HASH_OFFSET,
150
+ PROTOCOL_V2_RESOURCE_PACKAGE_HEADER_HASH_OFFSET + PROTOCOL_V2_RESOURCE_PACKAGE_HASH_SIZE
151
+ )
152
+ ),
160
153
  };
161
154
  }
162
155
 
163
- export function selectProtocolV2ResourceManifestFiles({
164
- manifest,
165
- targetsToUpdate,
166
- }: {
167
- manifest: IProtocolV2ResourceManifest;
168
- targetsToUpdate: readonly FirmwareUpdateV4Target[];
169
- }): IProtocolV2ResourceManifestFile[] {
170
- return targetsToUpdate.includes('resource') ? [...manifest.files] : [];
156
+ export function parseProtocolV2ResourcePackage(
157
+ binary: ArrayBuffer | Uint8Array
158
+ ): ProtocolV2ResourcePackageHeader {
159
+ const bytes = binary instanceof Uint8Array ? binary : new Uint8Array(binary);
160
+ return parseProtocolV2ResourcePackageHeader(bytes, bytes.byteLength);
171
161
  }
@@ -74,21 +74,6 @@ export type IProtocolV2Resources = {
74
74
  source: IProtocolV2ResourceSource;
75
75
  };
76
76
 
77
- export type IProtocolV2ResourceManifestFile = {
78
- archive_path: string;
79
- original_name?: string;
80
- device_path: string;
81
- size: number;
82
- sha256: string;
83
- signed?: boolean;
84
- sig_algo?: string;
85
- payload_version?: string | null;
86
- };
87
-
88
- export type IProtocolV2ResourceManifest = {
89
- files: IProtocolV2ResourceManifestFile[];
90
- };
91
-
92
77
  /** STM32 firmware config */
93
78
  export type IFirmwareReleaseInfo = {
94
79
  required: boolean;