@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.
Files changed (53) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +285 -13
  2. package/__tests__/check-firmware-release-protocol-v2.test.ts +6 -0
  3. package/__tests__/firmware-memory-host.test.ts +105 -0
  4. package/__tests__/firmware-update/firmware-update-plan.test.ts +15 -70
  5. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
  6. package/__tests__/protocol-v2-resources.test.ts +0 -38
  7. package/__tests__/protocol-v2.test.ts +212 -320
  8. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  9. package/dist/api/FirmwareUpdateV4.d.ts +1 -2
  10. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  11. package/dist/api/firmware/FirmwareMemoryHost.d.ts +27 -0
  12. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
  13. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +33 -1
  14. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  15. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  16. package/dist/api/firmware/getBinary.d.ts +0 -1
  17. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  18. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -1
  19. package/dist/index.d.ts +25 -64
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +546 -387
  22. package/dist/inject.d.ts.map +1 -1
  23. package/dist/protocols/protocol-v2/resources.d.ts +0 -17
  24. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  25. package/dist/types/api/checkAllFirmwareRelease.d.ts +1 -0
  26. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  27. package/dist/types/api/export.d.ts +0 -1
  28. package/dist/types/api/export.d.ts.map +1 -1
  29. package/dist/types/api/firmwareUpdate.d.ts +0 -10
  30. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  31. package/dist/types/api/index.d.ts +0 -2
  32. package/dist/types/api/index.d.ts.map +1 -1
  33. package/dist/types/settings.d.ts +0 -11
  34. package/dist/types/settings.d.ts.map +1 -1
  35. package/package.json +4 -4
  36. package/src/api/CheckAllFirmwareRelease.ts +72 -5
  37. package/src/api/FirmwareUpdateV4.ts +154 -165
  38. package/src/api/firmware/FirmwareMemoryHost.ts +171 -0
  39. package/src/api/firmware/FirmwareUpdatePlan.ts +176 -79
  40. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +8 -1
  41. package/src/api/firmware/protocolV2Release.ts +2 -0
  42. package/src/index.ts +6 -1
  43. package/src/inject.ts +0 -2
  44. package/src/protocols/protocol-v2/resources.ts +0 -47
  45. package/src/types/api/checkAllFirmwareRelease.ts +1 -0
  46. package/src/types/api/export.ts +0 -4
  47. package/src/types/api/firmwareUpdate.ts +0 -15
  48. package/src/types/api/index.ts +0 -2
  49. package/src/types/settings.ts +0 -20
  50. package/src/utils/deviceFeaturesUtils.ts +2 -2
  51. package/dist/types/api/protocolV2ResourceManifest.d.ts +0 -17
  52. package/dist/types/api/protocolV2ResourceManifest.d.ts.map +0 -1
  53. 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 { isProtocolV2ResourceFileValid } from '../protocols/protocol-v2/resources';
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 { openFirmwareByteSource, writeFirmwareByteSource } from './firmware/FirmwareArtifactSource';
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 || payload.resourceBundleArtifacts
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: payload.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(payload.componentArtifacts ?? {}).flatMap(([target, artifact]) =>
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
- : !this.hasExplicitProtocolV2Payload(explicitInstallItems);
694
- if (needsPreparedResources) {
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
- firmwareType: EFirmwareType,
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 release = DataManager.getFirmwareLatestRelease(features, firmwareType);
865
- const descriptors = release?.resourceBundles ?? [];
866
- const artifactByName = new Map(
867
- preparedArtifacts.map(item => [item.name, item.artifact] as const)
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 descriptor of descriptors) {
871
- const artifact = artifactByName.get(descriptor.name);
872
- if (!artifact) {
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 bundle ${descriptor.name} is not prepared`,
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
- const devicePath = validateProtocolV2FilesystemPath(
883
- descriptor.devicePath,
884
- 'resourceBundles[].devicePath'
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: descriptor.name,
888
- source: await this.openProtocolV2PreparedSource(artifact),
889
- devicePath,
890
- version: descriptor.version,
891
- payloadHash: descriptor.payloadHash,
892
- headerHash: descriptor.headerHash,
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 explicitResourceBundles = this.prepareExplicitProtocolV2ResourceFiles();
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
+ }