@onekeyfe/hd-core 1.2.0-alpha.142 → 1.2.0-alpha.143
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/__tests__/device-lifecycle-events.test.ts +23 -1
- package/__tests__/protocol-v2-resources.test.ts +35 -89
- package/__tests__/protocol-v2.test.ts +47 -187
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +117 -240
- package/dist/protocols/protocol-v2/resources.d.ts +11 -7
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/types/settings.d.ts +0 -13
- package/dist/types/settings.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +61 -238
- package/src/core/index.ts +5 -2
- package/src/index.ts +0 -4
- package/src/protocols/protocol-v2/resources.ts +92 -102
- package/src/types/settings.ts +0 -15
|
@@ -1,29 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
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
|
|
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 ===
|
|
66
|
+
if (path === PROTOCOL_V2_BOOT_RESOURCE_PACKAGE_STAGING_PATH) {
|
|
76
67
|
return true;
|
|
77
68
|
}
|
|
78
|
-
return
|
|
69
|
+
return (
|
|
70
|
+
path.endsWith('.okpkg') && PROTOCOL_V2_RESOURCE_DEVICE_ROOTS.some(root => path.startsWith(root))
|
|
71
|
+
);
|
|
79
72
|
}
|
|
80
73
|
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
89
|
-
const
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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(
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
const
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
129
|
+
throw new Error('Invalid Pro2 RESOURCE package header');
|
|
157
130
|
}
|
|
131
|
+
|
|
132
|
+
const packedVersion = view.getUint32(0x10, true);
|
|
158
133
|
return {
|
|
159
|
-
|
|
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
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
}
|
package/src/types/settings.ts
CHANGED
|
@@ -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;
|