@onekeyfe/hd-core 1.2.0-alpha.82 → 1.2.0-alpha.85
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__/check-all-firmware-release-protocol-v2.test.ts +285 -13
- package/__tests__/check-firmware-release-protocol-v2.test.ts +6 -0
- package/__tests__/firmware-memory-host.test.ts +105 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +15 -70
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
- package/__tests__/protocol-v2-resources.test.ts +0 -38
- package/__tests__/protocol-v2.test.ts +212 -320
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +1 -2
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareMemoryHost.d.ts +27 -0
- package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +33 -1
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +0 -1
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/protocolV2Release.d.ts.map +1 -1
- package/dist/index.d.ts +25 -64
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +546 -387
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +0 -17
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -0
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +0 -1
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +0 -10
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +0 -2
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/settings.d.ts +0 -11
- package/dist/types/settings.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +72 -5
- package/src/api/FirmwareUpdateV4.ts +154 -165
- package/src/api/firmware/FirmwareMemoryHost.ts +171 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +176 -79
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +8 -1
- package/src/api/firmware/protocolV2Release.ts +2 -0
- package/src/index.ts +6 -1
- package/src/inject.ts +0 -2
- package/src/protocols/protocol-v2/resources.ts +0 -47
- package/src/types/api/checkAllFirmwareRelease.ts +1 -0
- package/src/types/api/export.ts +0 -4
- package/src/types/api/firmwareUpdate.ts +0 -15
- package/src/types/api/index.ts +0 -2
- package/src/types/settings.ts +0 -20
- package/src/utils/deviceFeaturesUtils.ts +2 -2
- package/dist/types/api/protocolV2ResourceManifest.d.ts +0 -17
- package/dist/types/api/protocolV2ResourceManifest.d.ts.map +0 -1
- package/src/types/api/protocolV2ResourceManifest.ts +0 -19
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
import { sha256 } from '@noble/hashes/sha256';
|
|
10
10
|
|
|
11
11
|
import { FirmwareUpdateTipMessage, UI_REQUEST } from '../events/ui-request';
|
|
12
|
-
import { validateProtocolV2FilesystemPath } from './helpers/filesystemValidation';
|
|
13
12
|
import { validateParams } from './helpers/paramsValidator';
|
|
14
13
|
import {
|
|
15
14
|
LoggerNames,
|
|
@@ -30,16 +29,24 @@ import {
|
|
|
30
29
|
ProtocolV2FirmwareTargetType,
|
|
31
30
|
} from '../protocols/protocol-v2';
|
|
32
31
|
import { requestProtocolV2DeviceInfo } from '../protocols/protocol-v2/features';
|
|
33
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
parseProtocolV2ResourceManifest,
|
|
34
|
+
selectProtocolV2ResourceManifestFiles,
|
|
35
|
+
} from '../protocols/protocol-v2/resources';
|
|
34
36
|
import {
|
|
35
37
|
getProtocolV2UnknownErrorText,
|
|
36
38
|
isProtocolV2DeviceDisconnectedError,
|
|
37
39
|
} from './protocol-v2/helpers';
|
|
38
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
openFirmwareByteSource,
|
|
42
|
+
readFirmwareByteSourceFully,
|
|
43
|
+
writeFirmwareByteSource,
|
|
44
|
+
} from './firmware/FirmwareArtifactSource';
|
|
39
45
|
import { resolveFirmwareUpdateHostBinding } from './firmware/FirmwareHostBinding';
|
|
40
46
|
import {
|
|
41
47
|
assertFirmwareUpdatePreparedPlanBinding,
|
|
42
48
|
assertFirmwareUpdatePreparedPlanDeviceIdentity,
|
|
49
|
+
validateFirmwareUpdatePreparedPlan,
|
|
43
50
|
} from './firmware/FirmwareUpdatePreparedPlan';
|
|
44
51
|
|
|
45
52
|
import type {
|
|
@@ -88,6 +95,9 @@ const PROTOCOL_V2_OKPP_HEADER_SIZE = 0x52a0;
|
|
|
88
95
|
const PROTOCOL_V2_OKPP_PAYLOAD_HASH_OFFSET = 0x200;
|
|
89
96
|
const PROTOCOL_V2_OKPP_HEADER_HASH_OFFSET = 0x240;
|
|
90
97
|
const PROTOCOL_V2_OKPP_HASH_SIZE = 64;
|
|
98
|
+
const PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES = 1024 * 1024;
|
|
99
|
+
const PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT = 512;
|
|
100
|
+
const PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES = 256 * 1024 * 1024;
|
|
91
101
|
|
|
92
102
|
const PROTOCOL_V2_NEO_UNSUPPORTED_TARGETS = new Set<FirmwareUpdateV4Target>(['se03', 'se04']);
|
|
93
103
|
|
|
@@ -141,15 +151,6 @@ type ProtocolV2TargetBinary = { fileName: string; binary: ArrayBuffer; targetId:
|
|
|
141
151
|
type ProtocolV2InstallItem = ProtocolV2TargetBinary & {
|
|
142
152
|
kind: ProtocolV2RemoteComponentTarget['kind'];
|
|
143
153
|
};
|
|
144
|
-
/** Protocol V2 resource file written independently to devicePath through FileWrite. */
|
|
145
|
-
type ProtocolV2ResourceBundleBinary = {
|
|
146
|
-
name: string;
|
|
147
|
-
binary: ArrayBuffer;
|
|
148
|
-
devicePath: string;
|
|
149
|
-
version?: IVersionArray;
|
|
150
|
-
payloadHash?: string;
|
|
151
|
-
headerHash?: string;
|
|
152
|
-
};
|
|
153
154
|
|
|
154
155
|
type ProtocolV2RemoteComponentBinary = ProtocolV2RemoteComponentTarget & {
|
|
155
156
|
binary: ArrayBuffer;
|
|
@@ -544,7 +545,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
544
545
|
{ name: 'targetsToUpdate', type: 'array', allowEmpty: true },
|
|
545
546
|
{ name: 'platform', type: 'string' },
|
|
546
547
|
{ name: 'expectedDeviceId', type: 'string' },
|
|
547
|
-
{ name: 'resourceFiles', type: 'array', allowEmpty: true },
|
|
548
548
|
]);
|
|
549
549
|
if (
|
|
550
550
|
payload.expectedDeviceId !== undefined &&
|
|
@@ -556,27 +556,21 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
556
556
|
);
|
|
557
557
|
}
|
|
558
558
|
const hostBinding =
|
|
559
|
-
payload.preparedPlan || payload.componentArtifacts
|
|
559
|
+
payload.preparedPlan || payload.componentArtifacts
|
|
560
560
|
? resolveFirmwareUpdateHostBinding(payload.hostBindingGeneration)
|
|
561
561
|
: undefined;
|
|
562
562
|
if (hostBinding) {
|
|
563
|
+
const componentArtifacts = (payload.componentArtifacts ?? {}) as NonNullable<
|
|
564
|
+
FirmwareUpdateV4Params['componentArtifacts']
|
|
565
|
+
>;
|
|
566
|
+
const preparedPlan = validateFirmwareUpdatePreparedPlan(payload.preparedPlan);
|
|
563
567
|
assertFirmwareUpdatePreparedPlanBinding({
|
|
564
|
-
preparedPlan
|
|
568
|
+
preparedPlan,
|
|
565
569
|
executor: 'v4',
|
|
566
570
|
platform: payload.platform,
|
|
567
|
-
scopeTargets: [
|
|
568
|
-
'boot',
|
|
569
|
-
'app_v1',
|
|
570
|
-
'app_v2',
|
|
571
|
-
'coprocessor',
|
|
572
|
-
'resource',
|
|
573
|
-
'se01',
|
|
574
|
-
'se02',
|
|
575
|
-
'se03',
|
|
576
|
-
'se04',
|
|
577
|
-
],
|
|
571
|
+
scopeTargets: ['boot', 'app_v1', 'app_v2', 'coprocessor', 'se01', 'se02', 'se03', 'se04'],
|
|
578
572
|
bindings: [
|
|
579
|
-
...Object.entries(
|
|
573
|
+
...Object.entries(componentArtifacts).flatMap(([target, artifact]) =>
|
|
580
574
|
artifact
|
|
581
575
|
? [
|
|
582
576
|
{
|
|
@@ -586,19 +580,14 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
586
580
|
]
|
|
587
581
|
: []
|
|
588
582
|
),
|
|
589
|
-
...(payload.resourceBundleArtifacts ?? []).map(
|
|
590
|
-
(entry: { name: string; artifact: FirmwareArtifactReference }) => ({
|
|
591
|
-
target: 'resource' as const,
|
|
592
|
-
logicalName: entry.name,
|
|
593
|
-
artifact: entry.artifact,
|
|
594
|
-
})
|
|
595
|
-
),
|
|
596
583
|
],
|
|
597
584
|
});
|
|
598
585
|
}
|
|
599
586
|
|
|
600
587
|
this.params = {
|
|
601
|
-
preparedPlan: payload.preparedPlan
|
|
588
|
+
preparedPlan: payload.preparedPlan
|
|
589
|
+
? validateFirmwareUpdatePreparedPlan(payload.preparedPlan)
|
|
590
|
+
: undefined,
|
|
602
591
|
chunkSize: payload.chunkSize,
|
|
603
592
|
forcedUpdateRes: payload.forcedUpdateRes,
|
|
604
593
|
bootloaderBinary: payload.bootloaderBinary,
|
|
@@ -610,7 +599,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
610
599
|
se02Binary: payload.se02Binary,
|
|
611
600
|
se03Binary: payload.se03Binary,
|
|
612
601
|
se04Binary: payload.se04Binary,
|
|
613
|
-
resourceFiles: payload.resourceFiles,
|
|
614
602
|
firmwareType: payload.firmwareType,
|
|
615
603
|
targetsToUpdate: payload.targetsToUpdate,
|
|
616
604
|
expectedTargetVersions: payload.expectedTargetVersions,
|
|
@@ -618,7 +606,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
618
606
|
expectedDeviceId: payload.expectedDeviceId,
|
|
619
607
|
artifactReader: hostBinding?.artifactReader ?? payload.artifactReader,
|
|
620
608
|
componentArtifacts: payload.componentArtifacts,
|
|
621
|
-
resourceBundleArtifacts: payload.resourceBundleArtifacts,
|
|
622
609
|
};
|
|
623
610
|
}
|
|
624
611
|
|
|
@@ -664,23 +651,16 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
664
651
|
const firmwareType = this.params.firmwareType ?? deviceFirmwareType;
|
|
665
652
|
this.validateExpectedTargetVersions();
|
|
666
653
|
|
|
667
|
-
if (
|
|
668
|
-
(this.params.componentArtifacts && Object.keys(this.params.componentArtifacts).length > 0) ||
|
|
669
|
-
this.params.resourceBundleArtifacts?.length
|
|
670
|
-
) {
|
|
654
|
+
if (this.params.preparedPlan) {
|
|
671
655
|
return this.runProtocolV2PreparedArtifacts(deviceFeatures, firmwareType);
|
|
672
656
|
}
|
|
673
|
-
const hasExplicitResourceFiles = !!this.params.resourceFiles?.length;
|
|
674
657
|
const wantsResources = !!this.params.targetsToUpdate?.includes('resource');
|
|
675
|
-
const needsPreparedResources = !hasExplicitResourceFiles && wantsResources;
|
|
676
658
|
|
|
677
659
|
let fwBinaryMap: ProtocolV2TargetBinary[] = [];
|
|
678
660
|
let bootloaderBinary: ArrayBuffer | null = null;
|
|
679
661
|
let installItems: ProtocolV2InstallItem[] | undefined;
|
|
680
|
-
let resourceBundles: ProtocolV2ResourceBundleBinary[] | undefined;
|
|
681
662
|
try {
|
|
682
663
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
683
|
-
resourceBundles = this.prepareExplicitProtocolV2ResourceFiles();
|
|
684
664
|
fwBinaryMap = this.collectExplicitTargetBinaries();
|
|
685
665
|
bootloaderBinary = this.prepareBootloaderBinary();
|
|
686
666
|
const explicitInstallItems = this.buildProtocolV2InstallItems({
|
|
@@ -690,8 +670,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
690
670
|
const missingFirmwareTargets = this.getMissingProtocolV2FirmwareTargets(explicitInstallItems);
|
|
691
671
|
const needsRemoteFirmware = this.params.targetsToUpdate?.length
|
|
692
672
|
? missingFirmwareTargets.length > 0
|
|
693
|
-
:
|
|
694
|
-
if (
|
|
673
|
+
: explicitInstallItems.length === 0;
|
|
674
|
+
if (wantsResources) {
|
|
695
675
|
throw ERRORS.TypedError(
|
|
696
676
|
HardwareErrorCode.RuntimeError,
|
|
697
677
|
'Protocol V2 resource manifest must be prepared by the external firmware host',
|
|
@@ -743,12 +723,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
743
723
|
throw normalizeFirmwarePreparationError(err);
|
|
744
724
|
}
|
|
745
725
|
|
|
746
|
-
if (
|
|
747
|
-
!bootloaderBinary &&
|
|
748
|
-
fwBinaryMap.length === 0 &&
|
|
749
|
-
!installItems?.length &&
|
|
750
|
-
!resourceBundles?.length
|
|
751
|
-
) {
|
|
726
|
+
if (!bootloaderBinary && fwBinaryMap.length === 0 && !installItems?.length) {
|
|
752
727
|
throw ERRORS.TypedError(
|
|
753
728
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
754
729
|
'No firmware to update'
|
|
@@ -759,7 +734,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
759
734
|
fwBinaryMap,
|
|
760
735
|
bootloaderBinary,
|
|
761
736
|
...(installItems ? { installItems } : undefined),
|
|
762
|
-
...(resourceBundles?.length ? { resourceBundles } : undefined),
|
|
763
737
|
});
|
|
764
738
|
}
|
|
765
739
|
|
|
@@ -851,53 +825,138 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
851
825
|
return installSources;
|
|
852
826
|
}
|
|
853
827
|
|
|
854
|
-
private async prepareProtocolV2ResourceSources(
|
|
855
|
-
|
|
856
|
-
features: Features
|
|
857
|
-
): Promise<ProtocolV2ResourceBundleSource[]> {
|
|
858
|
-
const preparedArtifacts = this.params.resourceBundleArtifacts ?? [];
|
|
859
|
-
const resourceRequested =
|
|
860
|
-
this.params.targetsToUpdate?.includes('resource') || preparedArtifacts.length > 0;
|
|
828
|
+
private async prepareProtocolV2ResourceSources(): Promise<ProtocolV2ResourceBundleSource[]> {
|
|
829
|
+
const resourceRequested = this.params.targetsToUpdate?.includes('resource') ?? false;
|
|
861
830
|
if (!resourceRequested) {
|
|
862
831
|
return [];
|
|
863
832
|
}
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
833
|
+
const archiveSources = await this.prepareProtocolV2ResourceArchiveSources();
|
|
834
|
+
if (archiveSources) {
|
|
835
|
+
return archiveSources;
|
|
836
|
+
}
|
|
837
|
+
throw ERRORS.TypedError(
|
|
838
|
+
HardwareErrorCode.RuntimeError,
|
|
839
|
+
'Protocol V2 resource archive is not prepared',
|
|
840
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
868
841
|
);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
private async prepareProtocolV2ResourceArchiveSources(): Promise<
|
|
845
|
+
ProtocolV2ResourceBundleSource[] | undefined
|
|
846
|
+
> {
|
|
847
|
+
const archiveArtifacts =
|
|
848
|
+
this.params.preparedPlan?.artifacts.filter(
|
|
849
|
+
artifact =>
|
|
850
|
+
artifact.role === 'resourceBundle' &&
|
|
851
|
+
artifact.target === 'resource' &&
|
|
852
|
+
artifact.container === 'zip'
|
|
853
|
+
) ?? [];
|
|
854
|
+
if (archiveArtifacts.length === 0) {
|
|
855
|
+
return undefined;
|
|
856
|
+
}
|
|
857
|
+
if (archiveArtifacts.length !== 1) {
|
|
858
|
+
throw ERRORS.TypedError(
|
|
859
|
+
HardwareErrorCode.RuntimeError,
|
|
860
|
+
'Protocol V2 prepared plan must contain exactly one resource archive',
|
|
861
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const entries = archiveArtifacts[0].materializedEntries ?? [];
|
|
866
|
+
const manifestEntry = entries.find(entry => entry.entryName === 'manifest.json');
|
|
867
|
+
if (
|
|
868
|
+
!manifestEntry ||
|
|
869
|
+
manifestEntry.artifact.size <= 0 ||
|
|
870
|
+
manifestEntry.artifact.size > PROTOCOL_V2_RESOURCE_MANIFEST_MAX_BYTES
|
|
871
|
+
) {
|
|
872
|
+
throw ERRORS.TypedError(
|
|
873
|
+
HardwareErrorCode.RuntimeError,
|
|
874
|
+
'Protocol V2 prepared resource archive has no valid manifest.json',
|
|
875
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
const manifestSource = await this.openProtocolV2PreparedSource(manifestEntry.artifact);
|
|
880
|
+
let manifestValue: unknown;
|
|
881
|
+
try {
|
|
882
|
+
manifestValue = JSON.parse(
|
|
883
|
+
new TextDecoder().decode(await readFirmwareByteSourceFully(manifestSource))
|
|
884
|
+
);
|
|
885
|
+
} catch (error) {
|
|
886
|
+
throw ERRORS.TypedError(
|
|
887
|
+
HardwareErrorCode.RuntimeError,
|
|
888
|
+
`Protocol V2 prepared resource manifest is invalid: ${String(error)}`,
|
|
889
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
const manifest = parseProtocolV2ResourceManifest(manifestValue);
|
|
893
|
+
const selectedFiles = selectProtocolV2ResourceManifestFiles({
|
|
894
|
+
manifest,
|
|
895
|
+
targetsToUpdate: this.params.targetsToUpdate ?? [],
|
|
896
|
+
});
|
|
897
|
+
if (selectedFiles.length > PROTOCOL_V2_RESOURCE_FILE_MAX_COUNT) {
|
|
898
|
+
throw ERRORS.TypedError(
|
|
899
|
+
HardwareErrorCode.RuntimeError,
|
|
900
|
+
'Protocol V2 prepared resource archive contains too many files',
|
|
901
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const entriesByName = new Map(entries.map(entry => [entry.entryName, entry] as const));
|
|
906
|
+
const expectedEntryNames = new Set([
|
|
907
|
+
'manifest.json',
|
|
908
|
+
...selectedFiles.map(file => file.archive_path),
|
|
909
|
+
]);
|
|
910
|
+
if (entries.some(entry => !expectedEntryNames.has(entry.entryName))) {
|
|
911
|
+
throw ERRORS.TypedError(
|
|
912
|
+
HardwareErrorCode.RuntimeError,
|
|
913
|
+
'Protocol V2 prepared resource archive contains an unexpected entry',
|
|
914
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
let totalSize = 0;
|
|
869
919
|
const sources: ProtocolV2ResourceBundleSource[] = [];
|
|
870
|
-
for (const
|
|
871
|
-
const
|
|
872
|
-
if (
|
|
920
|
+
for (const [index, file] of selectedFiles.entries()) {
|
|
921
|
+
const entry = entriesByName.get(file.archive_path);
|
|
922
|
+
if (
|
|
923
|
+
!entry ||
|
|
924
|
+
entry.artifact.size !== file.size ||
|
|
925
|
+
entry.artifact.sha256.toLowerCase() !== file.sha256.toLowerCase()
|
|
926
|
+
) {
|
|
873
927
|
throw ERRORS.TypedError(
|
|
874
928
|
HardwareErrorCode.RuntimeError,
|
|
875
|
-
`Protocol V2 resource
|
|
876
|
-
{
|
|
877
|
-
firmwareUpdateCode: 'FirmwareArtifactsNotPrepared',
|
|
878
|
-
artifactName: descriptor.name,
|
|
879
|
-
}
|
|
929
|
+
`Protocol V2 prepared resource file does not match manifest: ${file.archive_path}`,
|
|
930
|
+
{ firmwareUpdateCode: 'FirmwareArtifactReceiptMismatch' }
|
|
880
931
|
);
|
|
881
932
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
933
|
+
totalSize += entry.artifact.size;
|
|
934
|
+
if (totalSize > PROTOCOL_V2_RESOURCE_TOTAL_MAX_BYTES) {
|
|
935
|
+
throw ERRORS.TypedError(
|
|
936
|
+
HardwareErrorCode.RuntimeError,
|
|
937
|
+
'Protocol V2 prepared resource archive exceeds the total size limit',
|
|
938
|
+
{ firmwareUpdateCode: 'FirmwareArtifactsNotPrepared' }
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
const source = await this.openProtocolV2PreparedSource(entry.artifact);
|
|
942
|
+
const header =
|
|
943
|
+
source.size >= PROTOCOL_V2_OKPP_HEADER_SIZE
|
|
944
|
+
? parseProtocolV2OkppHeader(
|
|
945
|
+
new Uint8Array(await source.readAt(0, PROTOCOL_V2_OKPP_HEADER_SIZE))
|
|
946
|
+
)
|
|
947
|
+
: null;
|
|
886
948
|
sources.push({
|
|
887
|
-
name:
|
|
888
|
-
source
|
|
889
|
-
devicePath,
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
949
|
+
name: file.original_name || `resource-${index}`,
|
|
950
|
+
source,
|
|
951
|
+
devicePath: file.device_path,
|
|
952
|
+
...(header
|
|
953
|
+
? {
|
|
954
|
+
version: header.version,
|
|
955
|
+
payloadHash: header.payloadHash,
|
|
956
|
+
headerHash: header.headerHash,
|
|
957
|
+
}
|
|
958
|
+
: {}),
|
|
893
959
|
});
|
|
894
|
-
artifactByName.delete(descriptor.name);
|
|
895
|
-
}
|
|
896
|
-
if (artifactByName.size > 0) {
|
|
897
|
-
throw ERRORS.TypedError(
|
|
898
|
-
HardwareErrorCode.RuntimeError,
|
|
899
|
-
`Protocol V2 release does not contain resource bundle ${artifactByName.keys().next().value}`
|
|
900
|
-
);
|
|
901
960
|
}
|
|
902
961
|
return sources;
|
|
903
962
|
}
|
|
@@ -906,19 +965,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
906
965
|
try {
|
|
907
966
|
this.postTipMessage(FirmwareUpdateTipMessage.StartDownloadFirmware);
|
|
908
967
|
const installSources = await this.prepareProtocolV2InstallSources(firmwareType, features);
|
|
909
|
-
const
|
|
910
|
-
const resourceSources = explicitResourceBundles?.length
|
|
911
|
-
? await Promise.all(
|
|
912
|
-
explicitResourceBundles.map(async bundle => ({
|
|
913
|
-
name: bundle.name,
|
|
914
|
-
source: await this.openProtocolV2MemorySource(bundle.binary),
|
|
915
|
-
devicePath: bundle.devicePath,
|
|
916
|
-
version: bundle.version,
|
|
917
|
-
payloadHash: bundle.payloadHash,
|
|
918
|
-
headerHash: bundle.headerHash,
|
|
919
|
-
}))
|
|
920
|
-
)
|
|
921
|
-
: await this.prepareProtocolV2ResourceSources(firmwareType, features);
|
|
968
|
+
const resourceSources = await this.prepareProtocolV2ResourceSources();
|
|
922
969
|
if (installSources.length === 0 && resourceSources.length === 0) {
|
|
923
970
|
throw ERRORS.TypedError(
|
|
924
971
|
HardwareErrorCode.FirmwareUpdateDownloadFailed,
|
|
@@ -1034,13 +1081,13 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1034
1081
|
if (this.params.targetsToUpdate?.length) {
|
|
1035
1082
|
return [...new Set(this.params.targetsToUpdate)];
|
|
1036
1083
|
}
|
|
1084
|
+
if (this.params.preparedPlan?.targetsToUpdate.length) {
|
|
1085
|
+
return [...new Set(this.params.preparedPlan.targetsToUpdate)] as FirmwareUpdateV4Target[];
|
|
1086
|
+
}
|
|
1037
1087
|
const targets = new Set<FirmwareUpdateV4Target>();
|
|
1038
1088
|
Object.keys(this.params.componentArtifacts ?? {}).forEach(target =>
|
|
1039
1089
|
targets.add(target as FirmwareUpdateV4Target)
|
|
1040
1090
|
);
|
|
1041
|
-
if (this.params.resourceBundleArtifacts?.length || this.params.resourceFiles?.length) {
|
|
1042
|
-
targets.add('resource');
|
|
1043
|
-
}
|
|
1044
1091
|
if (this.params.bootloaderBinary) targets.add('boot');
|
|
1045
1092
|
if (this.params.applicationP1Binary) targets.add('app_v1');
|
|
1046
1093
|
if (this.params.applicationP2Binary) targets.add('app_v2');
|
|
@@ -1118,10 +1165,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1118
1165
|
return this.params.bootloaderBinary ?? null;
|
|
1119
1166
|
}
|
|
1120
1167
|
|
|
1121
|
-
private hasExplicitProtocolV2Payload(installItems: ProtocolV2InstallItem[]) {
|
|
1122
|
-
return !!this.params.resourceFiles?.length || installItems.length > 0;
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
1168
|
private getMissingProtocolV2FirmwareTargets(installItems: ProtocolV2InstallItem[]) {
|
|
1126
1169
|
const preparedTargets = new Set(
|
|
1127
1170
|
installItems.flatMap(item => {
|
|
@@ -1303,48 +1346,6 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1303
1346
|
};
|
|
1304
1347
|
}
|
|
1305
1348
|
|
|
1306
|
-
private prepareExplicitProtocolV2ResourceFiles(): ProtocolV2ResourceBundleBinary[] | undefined {
|
|
1307
|
-
const files = this.params.resourceFiles ?? [];
|
|
1308
|
-
if (!files?.length) return undefined;
|
|
1309
|
-
|
|
1310
|
-
const prepared = files.map((file, index) => {
|
|
1311
|
-
const devicePath = validateProtocolV2FilesystemPath(
|
|
1312
|
-
file.devicePath,
|
|
1313
|
-
`resourceFiles[${index}].devicePath`
|
|
1314
|
-
);
|
|
1315
|
-
const descriptor = file as typeof file & Partial<{ size: number; fileHash: string }>;
|
|
1316
|
-
if (descriptor.size !== undefined && descriptor.size !== file.binary.byteLength) {
|
|
1317
|
-
throw new Error(`resourceFiles[${index}] size mismatch`);
|
|
1318
|
-
}
|
|
1319
|
-
if (
|
|
1320
|
-
descriptor.fileHash &&
|
|
1321
|
-
!isProtocolV2ResourceFileValid(file.binary, {
|
|
1322
|
-
size: file.binary.byteLength,
|
|
1323
|
-
fileHash: descriptor.fileHash,
|
|
1324
|
-
})
|
|
1325
|
-
) {
|
|
1326
|
-
throw new Error(`resourceFiles[${index}] SHA-256 mismatch`);
|
|
1327
|
-
}
|
|
1328
|
-
const header = parseProtocolV2OkppHeader(toProtocolV2Bytes(file.binary));
|
|
1329
|
-
return {
|
|
1330
|
-
name: devicePath.split('/').pop() ?? devicePath,
|
|
1331
|
-
binary: file.binary,
|
|
1332
|
-
devicePath,
|
|
1333
|
-
...(header
|
|
1334
|
-
? {
|
|
1335
|
-
version: header.version,
|
|
1336
|
-
payloadHash: header.payloadHash,
|
|
1337
|
-
headerHash: header.headerHash,
|
|
1338
|
-
}
|
|
1339
|
-
: {}),
|
|
1340
|
-
};
|
|
1341
|
-
});
|
|
1342
|
-
if (new Set(prepared.map(file => file.devicePath)).size !== prepared.length) {
|
|
1343
|
-
throw new Error('resourceFiles contain duplicate devicePath values');
|
|
1344
|
-
}
|
|
1345
|
-
return prepared;
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
1349
|
private getProtocolV2ResourceFilePath(path: string) {
|
|
1349
1350
|
if (path.startsWith('vol')) return path;
|
|
1350
1351
|
if (path.startsWith('/')) return `vol0:${path}`;
|
|
@@ -1661,12 +1662,10 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1661
1662
|
fwBinaryMap,
|
|
1662
1663
|
bootloaderBinary,
|
|
1663
1664
|
installItems,
|
|
1664
|
-
resourceBundles,
|
|
1665
1665
|
}: {
|
|
1666
1666
|
fwBinaryMap?: ProtocolV2TargetBinary[];
|
|
1667
1667
|
bootloaderBinary?: ArrayBuffer | null;
|
|
1668
1668
|
installItems?: ProtocolV2InstallItem[];
|
|
1669
|
-
resourceBundles?: ProtocolV2ResourceBundleBinary[];
|
|
1670
1669
|
}) {
|
|
1671
1670
|
const memoryInstallItems =
|
|
1672
1671
|
installItems ??
|
|
@@ -1683,19 +1682,9 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
1683
1682
|
kind: item.kind,
|
|
1684
1683
|
}))
|
|
1685
1684
|
);
|
|
1686
|
-
const resourceSources = await Promise.all(
|
|
1687
|
-
(resourceBundles ?? []).map(async bundle => ({
|
|
1688
|
-
name: bundle.name,
|
|
1689
|
-
source: await this.openProtocolV2MemorySource(bundle.binary),
|
|
1690
|
-
devicePath: bundle.devicePath,
|
|
1691
|
-
version: bundle.version,
|
|
1692
|
-
payloadHash: bundle.payloadHash,
|
|
1693
|
-
headerHash: bundle.headerHash,
|
|
1694
|
-
}))
|
|
1695
|
-
);
|
|
1696
1685
|
return await this.executeProtocolV2Phases({
|
|
1697
1686
|
installSources,
|
|
1698
|
-
resourceSources,
|
|
1687
|
+
resourceSources: [],
|
|
1699
1688
|
});
|
|
1700
1689
|
} finally {
|
|
1701
1690
|
await this.closeProtocolV2PreparedSources();
|
|
@@ -0,0 +1,171 @@
|
|
|
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
|
+
FirmwareUpdateV4Target,
|
|
10
|
+
} from '../../types/api/firmwareUpdate';
|
|
11
|
+
import type { FirmwareUpdatePlan } from '../../types/api/firmwareUpdatePlan';
|
|
12
|
+
|
|
13
|
+
export type FirmwareMemoryArtifactEntry = {
|
|
14
|
+
entryName: string;
|
|
15
|
+
binary: ArrayBuffer;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type FirmwareMemoryArtifact = {
|
|
19
|
+
artifactId: string;
|
|
20
|
+
binary: ArrayBuffer;
|
|
21
|
+
materializedEntries?: FirmwareMemoryArtifactEntry[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type FirmwareUpdateV4MemoryHost = {
|
|
25
|
+
preparedPlan: ReturnType<CoreApi['prepareFirmwareUpdatePlan']>;
|
|
26
|
+
hostBindingGeneration: number;
|
|
27
|
+
targetsToUpdate: FirmwareUpdateV4Target[];
|
|
28
|
+
expectedDeviceId: string;
|
|
29
|
+
expectedTargetVersions: Partial<Record<FirmwareUpdateV4Target, string>>;
|
|
30
|
+
componentArtifacts: Partial<
|
|
31
|
+
Record<Exclude<FirmwareUpdateV4Target, 'resource'>, FirmwareArtifactReference>
|
|
32
|
+
>;
|
|
33
|
+
release: () => void;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
let memoryHostSequence = 0;
|
|
37
|
+
|
|
38
|
+
const FIRMWARE_UPDATE_V4_COMPONENT_TARGETS = new Set<Exclude<FirmwareUpdateV4Target, 'resource'>>([
|
|
39
|
+
'boot',
|
|
40
|
+
'app_v1',
|
|
41
|
+
'app_v2',
|
|
42
|
+
'coprocessor',
|
|
43
|
+
'se01',
|
|
44
|
+
'se02',
|
|
45
|
+
'se03',
|
|
46
|
+
'se04',
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
const isFirmwareUpdateV4ComponentTarget = (
|
|
50
|
+
target: string
|
|
51
|
+
): target is Exclude<FirmwareUpdateV4Target, 'resource'> =>
|
|
52
|
+
FIRMWARE_UPDATE_V4_COMPONENT_TARGETS.has(target as Exclude<FirmwareUpdateV4Target, 'resource'>);
|
|
53
|
+
|
|
54
|
+
const createReference = (binary: ArrayBuffer, prefix: string): FirmwareArtifactReference => {
|
|
55
|
+
const digest = bytesToHex(sha256(new Uint8Array(binary)));
|
|
56
|
+
return {
|
|
57
|
+
artifactRef: `fwmem:${prefix}:${digest.slice(0, 32)}`,
|
|
58
|
+
size: binary.byteLength,
|
|
59
|
+
sha256: digest,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function prepareFirmwareUpdateV4MemoryHost({
|
|
64
|
+
sdk,
|
|
65
|
+
plan,
|
|
66
|
+
artifacts,
|
|
67
|
+
}: {
|
|
68
|
+
sdk: Pick<
|
|
69
|
+
CoreApi,
|
|
70
|
+
| 'prepareFirmwareUpdatePlan'
|
|
71
|
+
| 'registerFirmwareUpdateHostBinding'
|
|
72
|
+
| 'unregisterFirmwareUpdateHostBinding'
|
|
73
|
+
>;
|
|
74
|
+
plan: FirmwareUpdatePlan;
|
|
75
|
+
artifacts: FirmwareMemoryArtifact[];
|
|
76
|
+
}): FirmwareUpdateV4MemoryHost {
|
|
77
|
+
if (plan.executor !== 'v4') {
|
|
78
|
+
throw ERRORS.TypedError(
|
|
79
|
+
HardwareErrorCode.RuntimeError,
|
|
80
|
+
'Firmware memory host only supports V4 plans'
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
memoryHostSequence += 1;
|
|
84
|
+
const hostId = `${Date.now()}:${memoryHostSequence}`;
|
|
85
|
+
const binaries = new Map<string, Uint8Array>();
|
|
86
|
+
const inputs = artifacts.map((input, artifactIndex) => {
|
|
87
|
+
const artifact = createReference(input.binary, `${hostId}:artifact:${artifactIndex}`);
|
|
88
|
+
binaries.set(artifact.artifactRef, new Uint8Array(input.binary));
|
|
89
|
+
const materializedEntries = input.materializedEntries?.map((entry, entryIndex) => {
|
|
90
|
+
const entryArtifact = createReference(
|
|
91
|
+
entry.binary,
|
|
92
|
+
`${hostId}:entry:${artifactIndex}:${entryIndex}`
|
|
93
|
+
);
|
|
94
|
+
binaries.set(entryArtifact.artifactRef, new Uint8Array(entry.binary));
|
|
95
|
+
return {
|
|
96
|
+
entryName: entry.entryName,
|
|
97
|
+
artifact: entryArtifact,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
artifactId: input.artifactId,
|
|
102
|
+
artifact,
|
|
103
|
+
...(materializedEntries?.length ? { materializedEntries } : {}),
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
const preparedPlan = sdk.prepareFirmwareUpdatePlan({
|
|
107
|
+
plan,
|
|
108
|
+
leaseRef: `fwmemlease:${hostId}`,
|
|
109
|
+
artifacts: inputs,
|
|
110
|
+
});
|
|
111
|
+
const readers = new Map<string, Uint8Array>();
|
|
112
|
+
let readerSequence = 0;
|
|
113
|
+
const artifactReader: FirmwareArtifactReader = {
|
|
114
|
+
open({ artifactRef }) {
|
|
115
|
+
const binary = binaries.get(artifactRef);
|
|
116
|
+
if (!binary) {
|
|
117
|
+
throw ERRORS.TypedError(
|
|
118
|
+
HardwareErrorCode.RuntimeError,
|
|
119
|
+
'Firmware memory artifact is unavailable'
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
readerSequence += 1;
|
|
123
|
+
const readerId = `fwmemreader:${hostId}:${readerSequence}`;
|
|
124
|
+
readers.set(readerId, binary);
|
|
125
|
+
return Promise.resolve({ readerId, size: binary.byteLength });
|
|
126
|
+
},
|
|
127
|
+
read({ readerId, offset, length }) {
|
|
128
|
+
const binary = readers.get(readerId);
|
|
129
|
+
if (!binary || offset < 0 || length <= 0 || offset + length > binary.byteLength) {
|
|
130
|
+
throw ERRORS.TypedError(
|
|
131
|
+
HardwareErrorCode.RuntimeError,
|
|
132
|
+
'Firmware memory artifact read is invalid'
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
const data = binary.slice(offset, offset + length).buffer;
|
|
136
|
+
return Promise.resolve({
|
|
137
|
+
data,
|
|
138
|
+
bytesRead: data.byteLength,
|
|
139
|
+
eof: offset + length === binary.byteLength,
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
close({ readerId }) {
|
|
143
|
+
readers.delete(readerId);
|
|
144
|
+
return Promise.resolve();
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
const hostBindingGeneration = sdk.registerFirmwareUpdateHostBinding({ artifactReader });
|
|
148
|
+
const componentArtifacts: FirmwareUpdateV4MemoryHost['componentArtifacts'] = {};
|
|
149
|
+
const expectedTargetVersions: FirmwareUpdateV4MemoryHost['expectedTargetVersions'] = {};
|
|
150
|
+
for (const artifact of preparedPlan.artifacts) {
|
|
151
|
+
if (artifact.role === 'component' && isFirmwareUpdateV4ComponentTarget(artifact.target)) {
|
|
152
|
+
componentArtifacts[artifact.target] = artifact.artifact;
|
|
153
|
+
}
|
|
154
|
+
if (artifact.targetVersion) {
|
|
155
|
+
expectedTargetVersions[artifact.target as FirmwareUpdateV4Target] = artifact.targetVersion;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
preparedPlan,
|
|
160
|
+
hostBindingGeneration,
|
|
161
|
+
targetsToUpdate: [...preparedPlan.targetsToUpdate] as FirmwareUpdateV4Target[],
|
|
162
|
+
expectedDeviceId: preparedPlan.deviceIdentity,
|
|
163
|
+
expectedTargetVersions,
|
|
164
|
+
componentArtifacts,
|
|
165
|
+
release: () => {
|
|
166
|
+
sdk.unregisterFirmwareUpdateHostBinding(hostBindingGeneration);
|
|
167
|
+
readers.clear();
|
|
168
|
+
binaries.clear();
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|