@onekeyfe/hd-core 1.2.0-alpha.97 → 1.2.0-alpha.99

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 (103) hide show
  1. package/__tests__/check-all-firmware-release-protocol-v2.test.ts +411 -86
  2. package/__tests__/check-firmware-release-protocol-v2.test.ts +199 -0
  3. package/__tests__/firmware-memory-host.test.ts +126 -0
  4. package/__tests__/firmware-update/device-update-bootloader.test.ts +14 -2
  5. package/__tests__/firmware-update/firmware-host-binding.test.ts +9 -0
  6. package/__tests__/firmware-update/firmware-prepared-host-digest.test.ts +573 -0
  7. package/__tests__/firmware-update/firmware-prepared-resource-archive.test.ts +150 -0
  8. package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +65 -2
  9. package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +80 -0
  10. package/__tests__/firmware-update/firmware-update-plan.test.ts +293 -71
  11. package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +53 -1
  12. package/__tests__/firmware-update/firmware-update-prepared-resource-executors.test.ts +326 -0
  13. package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +2 -2
  14. package/__tests__/protocol-v2-firmware-targets.test.ts +16 -0
  15. package/__tests__/protocol-v2-resources.test.ts +148 -232
  16. package/__tests__/protocol-v2.test.ts +1326 -510
  17. package/dist/api/CheckAllFirmwareRelease.d.ts +2 -3
  18. package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
  19. package/dist/api/CheckBLEFirmwareRelease.d.ts +3 -2
  20. package/dist/api/CheckBLEFirmwareRelease.d.ts.map +1 -1
  21. package/dist/api/CheckBootloaderRelease.d.ts +2 -1
  22. package/dist/api/CheckBootloaderRelease.d.ts.map +1 -1
  23. package/dist/api/CheckFirmwareRelease.d.ts +3 -2
  24. package/dist/api/CheckFirmwareRelease.d.ts.map +1 -1
  25. package/dist/api/FirmwareUpdateV2.d.ts +1 -0
  26. package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
  27. package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
  28. package/dist/api/FirmwareUpdateV4.d.ts +11 -9
  29. package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
  30. package/dist/api/UploadPortfolio.d.ts.map +1 -1
  31. package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
  32. package/dist/api/firmware/FirmwareHostBinding.d.ts +1 -1
  33. package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -1
  34. package/dist/api/firmware/FirmwareMemoryHost.d.ts +22 -0
  35. package/dist/api/firmware/FirmwareMemoryHost.d.ts.map +1 -0
  36. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts +11 -0
  37. package/dist/api/firmware/FirmwarePreparedResourceArchive.d.ts.map +1 -0
  38. package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
  39. package/dist/api/firmware/FirmwareUpdatePlan.d.ts +48 -1
  40. package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -1
  41. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +7 -1
  42. package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -1
  43. package/dist/api/firmware/getBinary.d.ts +0 -1
  44. package/dist/api/firmware/getBinary.d.ts.map +1 -1
  45. package/dist/api/firmware/protocolV2Release.d.ts +33 -0
  46. package/dist/api/firmware/protocolV2Release.d.ts.map +1 -0
  47. package/dist/data-manager/DataManager.d.ts +4 -3
  48. package/dist/data-manager/DataManager.d.ts.map +1 -1
  49. package/dist/index.d.ts +108 -114
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +1825 -1006
  52. package/dist/protocols/protocol-v2/resources.d.ts +10 -28
  53. package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
  54. package/dist/types/api/checkAllFirmwareRelease.d.ts +20 -8
  55. package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
  56. package/dist/types/api/checkBLEFirmwareRelease.d.ts +2 -13
  57. package/dist/types/api/checkBLEFirmwareRelease.d.ts.map +1 -1
  58. package/dist/types/api/checkBootloaderRelease.d.ts +2 -6
  59. package/dist/types/api/checkBootloaderRelease.d.ts.map +1 -1
  60. package/dist/types/api/checkFirmwareRelease.d.ts +2 -13
  61. package/dist/types/api/checkFirmwareRelease.d.ts.map +1 -1
  62. package/dist/types/api/checkFirmwareTypeAvailable.d.ts +2 -2
  63. package/dist/types/api/checkFirmwareTypeAvailable.d.ts.map +1 -1
  64. package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
  65. package/dist/types/api/export.d.ts +1 -1
  66. package/dist/types/api/export.d.ts.map +1 -1
  67. package/dist/types/api/firmwareUpdate.d.ts +4 -12
  68. package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
  69. package/dist/types/api/firmwareUpdatePlan.d.ts +2 -2
  70. package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -1
  71. package/dist/types/settings.d.ts +35 -36
  72. package/dist/types/settings.d.ts.map +1 -1
  73. package/package.json +4 -4
  74. package/src/api/CheckAllFirmwareRelease.ts +113 -80
  75. package/src/api/CheckBLEFirmwareRelease.ts +37 -5
  76. package/src/api/CheckBootloaderRelease.ts +35 -3
  77. package/src/api/CheckFirmwareRelease.ts +35 -7
  78. package/src/api/FirmwareUpdateV2.ts +231 -138
  79. package/src/api/FirmwareUpdateV3.ts +101 -56
  80. package/src/api/FirmwareUpdateV4.ts +936 -408
  81. package/src/api/UploadPortfolio.ts +18 -0
  82. package/src/api/device/DeviceUpdateBootloader.ts +37 -11
  83. package/src/api/firmware/FirmwareHostBinding.ts +26 -3
  84. package/src/api/firmware/FirmwareMemoryHost.ts +143 -0
  85. package/src/api/firmware/FirmwarePreparedResourceArchive.ts +207 -0
  86. package/src/api/firmware/FirmwareUpdateBaseMethod.ts +122 -73
  87. package/src/api/firmware/FirmwareUpdatePlan.ts +295 -122
  88. package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +49 -12
  89. package/src/api/firmware/protocolV2Release.ts +168 -0
  90. package/src/data-manager/DataManager.ts +35 -19
  91. package/src/index.ts +10 -0
  92. package/src/protocols/protocol-v2/resources.ts +157 -341
  93. package/src/types/api/checkAllFirmwareRelease.ts +27 -12
  94. package/src/types/api/checkBLEFirmwareRelease.ts +4 -13
  95. package/src/types/api/checkBootloaderRelease.ts +2 -6
  96. package/src/types/api/checkFirmwareRelease.ts +2 -13
  97. package/src/types/api/checkFirmwareTypeAvailable.ts +2 -2
  98. package/src/types/api/deviceUpdateBootloader.ts +1 -0
  99. package/src/types/api/export.ts +6 -1
  100. package/src/types/api/firmwareUpdate.ts +12 -17
  101. package/src/types/api/firmwareUpdatePlan.ts +2 -2
  102. package/src/types/settings.ts +35 -61
  103. package/src/utils/deviceFeaturesUtils.ts +2 -2
@@ -1,6 +1,13 @@
1
1
  import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
2
 
3
- import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
3
+ import {
4
+ buildFirmwareUpdatePlan,
5
+ validateProtocolV2FirmwareUpdateTargets,
6
+ } from '../../src/api/firmware/FirmwareUpdatePlan';
7
+ import {
8
+ assertFirmwareUpdatePreparedPlanDeviceIdentity,
9
+ prepareFirmwareUpdatePlan,
10
+ } from '../../src/api/firmware/FirmwareUpdatePreparedPlan';
4
11
 
5
12
  import type { Features } from '../../src/types';
6
13
 
@@ -50,6 +57,8 @@ const createLegacyForceInput = (
50
57
  release: Record<string, unknown> = {
51
58
  url: 'https://firmware.onekey.so/pro/firmware.bin',
52
59
  version: [4, 21, 0],
60
+ expectedSize: 1024,
61
+ fingerprint: '1'.repeat(64),
53
62
  }
54
63
  ): BuildPlanInput => ({
55
64
  features: createFeatures({
@@ -82,6 +91,18 @@ const createProtocolV2ForceInput = (
82
91
  forceUpdateTargets: forceUpdateTargets as BuildPlanInput['forceUpdateTargets'],
83
92
  });
84
93
 
94
+ describe('validateProtocolV2FirmwareUpdateTargets', () => {
95
+ test('normalizes the deprecated boot_resources alias to resource', () => {
96
+ expect(validateProtocolV2FirmwareUpdateTargets(['boot_resources'])).toEqual(['resource']);
97
+ });
98
+
99
+ test('rejects duplicate semantic targets after alias normalization', () => {
100
+ expectFirmwarePlanInvalid(() =>
101
+ validateProtocolV2FirmwareUpdateTargets(['boot_resources', 'resource'])
102
+ );
103
+ });
104
+ });
105
+
85
106
  describe('buildFirmwareUpdatePlan', () => {
86
107
  test('selects all legacy artifacts before execution and chooses the desktop full resource', () => {
87
108
  const plan = buildFirmwareUpdatePlan({
@@ -168,6 +189,8 @@ describe('buildFirmwareUpdatePlan', () => {
168
189
  release: {
169
190
  url: 'https://firmware.onekey.so/pro/firmware.bin',
170
191
  version: [4, 0, 0],
192
+ expectedSize: 1024,
193
+ fingerprint: '1'.repeat(64),
171
194
  },
172
195
  },
173
196
  ble: noUpdate,
@@ -193,7 +216,11 @@ describe('buildFirmwareUpdatePlan', () => {
193
216
  release: {
194
217
  url: 'https://firmware.onekey.so/pro/firmware.bin',
195
218
  version: [4, 21, 0],
219
+ expectedSize: 1024,
220
+ fingerprint: '1'.repeat(64),
196
221
  resource: 'https://firmware.onekey.so/pro/resource.zip',
222
+ resourceExpectedSize: 4096,
223
+ resourceFingerprint: '1'.repeat(64),
197
224
  },
198
225
  },
199
226
  ble: {
@@ -201,6 +228,8 @@ describe('buildFirmwareUpdatePlan', () => {
201
228
  release: {
202
229
  webUpdate: 'https://firmware.onekey.so/pro/ble.bin',
203
230
  version: [2, 3, 7],
231
+ expectedSize: 512,
232
+ fingerprintWeb: '2'.repeat(64),
204
233
  },
205
234
  },
206
235
  bootloader: {
@@ -208,6 +237,8 @@ describe('buildFirmwareUpdatePlan', () => {
208
237
  release: {
209
238
  bootloaderResource: 'https://firmware.onekey.so/pro/bootloader.bin',
210
239
  bootloaderVersion: [2, 8, 4],
240
+ bootloaderExpectedSize: 768,
241
+ bootloaderFingerprint: '3'.repeat(64),
211
242
  },
212
243
  },
213
244
  forceUpdateTargets: ['firmware', 'resource', 'ble', 'bootloader'],
@@ -237,6 +268,8 @@ describe('buildFirmwareUpdatePlan', () => {
237
268
  release: {
238
269
  url: 'https://firmware.onekey.so/pro/firmware.bin',
239
270
  version: [4, 21, 0],
271
+ expectedSize: 1024,
272
+ fingerprint: '1'.repeat(64),
240
273
  resource: 'https://firmware.onekey.so/pro/resource.zip',
241
274
  },
242
275
  },
@@ -249,6 +282,32 @@ describe('buildFirmwareUpdatePlan', () => {
249
282
  expect(plan.targetsToUpdate).toEqual(['firmware']);
250
283
  });
251
284
 
285
+ test('does not approve an unexecutable V2 resource during bootloader recovery', () => {
286
+ const features = createFeatures({ deviceType: EDeviceType.Classic1s });
287
+ features.bootloaderMode = true;
288
+ const plan = buildFirmwareUpdatePlan({
289
+ features,
290
+ firmwareType: EFirmwareType.Universal,
291
+ platform: 'desktop',
292
+ firmware: {
293
+ status: 'none',
294
+ release: {
295
+ url: 'https://firmware.onekey.so/classic/firmware.bin',
296
+ expectedSize: 1024,
297
+ fingerprint: '1'.repeat(64),
298
+ resource: 'https://firmware.onekey.so/classic/resource.zip',
299
+ resourceExpectedSize: 4096,
300
+ resourceFingerprint: '2'.repeat(64),
301
+ },
302
+ },
303
+ ble: noUpdate,
304
+ bootloader: noUpdate,
305
+ });
306
+
307
+ expect(plan.artifacts.map(artifact => artifact.artifactId)).toEqual(['firmware']);
308
+ expect(plan.targetsToUpdate).toEqual(['firmware']);
309
+ });
310
+
252
311
  test.each([
253
312
  {
254
313
  label: 'a non-array value',
@@ -272,6 +331,110 @@ describe('buildFirmwareUpdatePlan', () => {
272
331
  expectFirmwarePlanInvalid(() => buildFirmwareUpdatePlan(createLegacyForceInput(['resource'])));
273
332
  });
274
333
 
334
+ test.each([
335
+ {
336
+ executor: 'V2',
337
+ deviceType: EDeviceType.Classic1s,
338
+ bootloaderVersion: '2.1.2',
339
+ },
340
+ {
341
+ executor: 'V3',
342
+ deviceType: EDeviceType.Pro,
343
+ bootloaderVersion: '2.8.4',
344
+ },
345
+ ])(
346
+ 'rejects a resource-only prepared plan that the $executor executor cannot run',
347
+ ({ deviceType, bootloaderVersion }) => {
348
+ expectFirmwarePlanInvalid(
349
+ () =>
350
+ buildFirmwareUpdatePlan({
351
+ features: createFeatures({
352
+ deviceType,
353
+ firmwareVersion: '4.21.0',
354
+ bootloaderVersion,
355
+ }),
356
+ firmwareType: EFirmwareType.Universal,
357
+ platform: 'desktop',
358
+ firmware: {
359
+ status: 'valid',
360
+ release: {
361
+ url: 'https://firmware.onekey.so/legacy/firmware.bin',
362
+ version: [4, 21, 0],
363
+ expectedSize: 1024,
364
+ fingerprint: '1'.repeat(64),
365
+ resource: 'https://firmware.onekey.so/legacy/resource.zip',
366
+ resourceExpectedSize: 4096,
367
+ resourceFingerprint: '2'.repeat(64),
368
+ },
369
+ },
370
+ ble: noUpdate,
371
+ bootloader: noUpdate,
372
+ forceUpdateTargets: ['resource'],
373
+ }),
374
+ 'Legacy resource updates require a firmware target'
375
+ );
376
+ }
377
+ );
378
+
379
+ test('rejects a legacy resource archive without complete integrity metadata', () => {
380
+ expectFirmwarePlanInvalid(
381
+ () =>
382
+ buildFirmwareUpdatePlan(
383
+ createLegacyForceInput(['resource'], {
384
+ url: 'https://firmware.onekey.so/pro/firmware.bin',
385
+ resource: 'https://firmware.onekey.so/pro/resource.zip',
386
+ resourceExpectedSize: 4096,
387
+ })
388
+ ),
389
+ 'integrity metadata is invalid'
390
+ );
391
+ });
392
+
393
+ test.each(['firmware', 'ble', 'bootloader'] as const)(
394
+ 'rejects a remote legacy %s artifact without complete integrity metadata',
395
+ target => {
396
+ expectFirmwarePlanInvalid(
397
+ () =>
398
+ buildFirmwareUpdatePlan({
399
+ features: createFeatures({ deviceType: EDeviceType.Classic1s }),
400
+ firmwareType: EFirmwareType.Universal,
401
+ platform: 'desktop',
402
+ firmware:
403
+ target === 'firmware'
404
+ ? {
405
+ status: 'outdated',
406
+ release: {
407
+ url: 'https://firmware.onekey.so/classic/firmware.bin',
408
+ expectedSize: 1024,
409
+ },
410
+ }
411
+ : noUpdate,
412
+ ble:
413
+ target === 'ble'
414
+ ? {
415
+ status: 'outdated',
416
+ release: {
417
+ webUpdate: 'https://firmware.onekey.so/classic/ble.bin',
418
+ expectedSize: 512,
419
+ },
420
+ }
421
+ : noUpdate,
422
+ bootloader:
423
+ target === 'bootloader'
424
+ ? {
425
+ status: 'outdated',
426
+ release: {
427
+ bootloaderResource: 'https://firmware.onekey.so/classic/bootloader.bin',
428
+ bootloaderExpectedSize: 768,
429
+ },
430
+ }
431
+ : noUpdate,
432
+ }),
433
+ 'integrity metadata is invalid'
434
+ );
435
+ }
436
+ );
437
+
275
438
  test.each(['ble', 'bootloader'] as const)(
276
439
  'rejects the unsupported Pro2 %s force target',
277
440
  forceTarget => {
@@ -297,12 +460,6 @@ describe('buildFirmwareUpdatePlan', () => {
297
460
  forceTarget: 'firmware' as const,
298
461
  release: {
299
462
  components: {},
300
- resourceBundles: [
301
- {
302
- name: 'images',
303
- url: 'https://firmware.onekey.so/pro2/images.okpkg',
304
- },
305
- ],
306
463
  },
307
464
  },
308
465
  {
@@ -312,6 +469,8 @@ describe('buildFirmwareUpdatePlan', () => {
312
469
  applicationP1: {
313
470
  target: 'APPLICATION_P1',
314
471
  url: 'https://firmware.onekey.so/pro2/application-p1.bin',
472
+ expectedSize: 1024,
473
+ fingerprint: '1111111111111111111111111111111111111111111111111111111111111111',
315
474
  },
316
475
  },
317
476
  },
@@ -325,41 +484,23 @@ describe('buildFirmwareUpdatePlan', () => {
325
484
  }
326
485
  );
327
486
 
328
- test.each([
329
- {
330
- forceTarget: 'firmware' as const,
331
- expectedArtifactIds: ['component:app_v1'],
332
- expectedTargets: ['app_v1'],
333
- },
334
- {
335
- forceTarget: 'resource' as const,
336
- expectedArtifactIds: ['resourceBundle:images'],
337
- expectedTargets: ['resource'],
338
- },
339
- ])(
340
- 'limits a Pro2 $forceTarget force to the selected artifact role',
341
- ({ forceTarget, expectedArtifactIds, expectedTargets }) => {
342
- const plan = buildFirmwareUpdatePlan(
343
- createProtocolV2ForceInput([forceTarget], {
344
- components: {
345
- applicationP1: {
346
- target: 'APPLICATION_P1',
347
- url: 'https://firmware.onekey.so/pro2/application-p1.bin',
348
- },
487
+ test('limits a Pro2 firmware force to component artifacts', () => {
488
+ const plan = buildFirmwareUpdatePlan(
489
+ createProtocolV2ForceInput(['firmware'], {
490
+ components: {
491
+ applicationP1: {
492
+ target: 'APPLICATION_P1',
493
+ url: 'https://firmware.onekey.so/pro2/application-p1.bin',
494
+ expectedSize: 1024,
495
+ fingerprint: '1111111111111111111111111111111111111111111111111111111111111111',
349
496
  },
350
- resourceBundles: [
351
- {
352
- name: 'images',
353
- url: 'https://firmware.onekey.so/pro2/images.okpkg',
354
- },
355
- ],
356
- })
357
- );
497
+ },
498
+ })
499
+ );
358
500
 
359
- expect(plan.artifacts.map(artifact => artifact.artifactId)).toEqual(expectedArtifactIds);
360
- expect(plan.targetsToUpdate).toEqual(expectedTargets);
361
- }
362
- );
501
+ expect(plan.artifacts.map(artifact => artifact.artifactId)).toEqual(['component:app_v1']);
502
+ expect(plan.targetsToUpdate).toEqual(['app_v1']);
503
+ });
363
504
 
364
505
  test('rejects a prepared native plan without a stable device identity', () => {
365
506
  const features = createFeatures({
@@ -376,6 +517,8 @@ describe('buildFirmwareUpdatePlan', () => {
376
517
  status: 'outdated',
377
518
  release: {
378
519
  url: 'https://firmware.onekey.so/classic/firmware.bin',
520
+ expectedSize: 1024,
521
+ fingerprint: '1'.repeat(64),
379
522
  },
380
523
  },
381
524
  ble: noUpdate,
@@ -384,7 +527,72 @@ describe('buildFirmwareUpdatePlan', () => {
384
527
  ).toThrow('requires a stable device identity');
385
528
  });
386
529
 
387
- test('maps the Pro2 component order and resource bundles without downloading them', () => {
530
+ test('allows an identity-less Protocol V2 device while retaining the model binding', () => {
531
+ const features = createFeatures({
532
+ deviceType: EDeviceType.Pro2,
533
+ });
534
+ delete (features as Partial<Features>).serialNo;
535
+
536
+ const plan = buildFirmwareUpdatePlan({
537
+ features,
538
+ firmwareType: EFirmwareType.Universal,
539
+ platform: 'desktop',
540
+ firmware: {
541
+ status: 'outdated',
542
+ release: {
543
+ components: {
544
+ applicationP1: {
545
+ target: 'APPLICATION_P1',
546
+ url: 'https://firmware.onekey.so/pro2/application-p1.bin',
547
+ expectedSize: 1024,
548
+ fingerprint: '1111111111111111111111111111111111111111111111111111111111111111',
549
+ },
550
+ },
551
+ },
552
+ },
553
+ ble: noUpdate,
554
+ bootloader: noUpdate,
555
+ });
556
+
557
+ expect(plan).toMatchObject({
558
+ executor: 'v4',
559
+ deviceIdentity: 'unavailable',
560
+ deviceModel: String(EDeviceType.Pro2),
561
+ });
562
+
563
+ const preparedPlan = prepareFirmwareUpdatePlan({
564
+ plan,
565
+ leaseRef: 'fwlease:00000000-0000-4000-8000-000000000004',
566
+ artifacts: [
567
+ {
568
+ artifactId: 'component:app_v1',
569
+ artifact: {
570
+ artifactRef: `fw:${'1'.repeat(64)}`,
571
+ size: 1024,
572
+ sha256: '1'.repeat(64),
573
+ },
574
+ },
575
+ ],
576
+ });
577
+ expect(() =>
578
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
579
+ preparedPlan,
580
+ deviceIdentity: undefined,
581
+ bootloaderMode: false,
582
+ deviceModel: String(EDeviceType.Pro2),
583
+ })
584
+ ).not.toThrow();
585
+ expect(() =>
586
+ assertFirmwareUpdatePreparedPlanDeviceIdentity({
587
+ preparedPlan,
588
+ deviceIdentity: undefined,
589
+ bootloaderMode: false,
590
+ deviceModel: String(EDeviceType.Neo),
591
+ })
592
+ ).toThrow();
593
+ });
594
+
595
+ test('maps the Pro2 component order without downloading artifacts', () => {
388
596
  const plan = buildFirmwareUpdatePlan({
389
597
  features: createFeatures({ deviceType: EDeviceType.Pro2 }),
390
598
  firmwareType: EFirmwareType.Universal,
@@ -403,18 +611,16 @@ describe('buildFirmwareUpdatePlan', () => {
403
611
  bootloader: {
404
612
  target: 'BOOTLOADER',
405
613
  url: 'https://firmware.onekey.so/pro2/bootloader.bin',
614
+ expectedSize: 2048,
615
+ fingerprint: '2222222222222222222222222222222222222222222222222222222222222222',
406
616
  },
407
617
  se01: {
408
618
  target: 'SE01',
409
619
  url: 'https://firmware.onekey.so/pro2/se01.bin',
620
+ expectedSize: 512,
621
+ fingerprint: '3333333333333333333333333333333333333333333333333333333333333333',
410
622
  },
411
623
  },
412
- resourceBundles: [
413
- {
414
- name: 'images',
415
- url: 'https://firmware.onekey.so/pro2/images.okpkg',
416
- },
417
- ],
418
624
  },
419
625
  },
420
626
  ble: noUpdate,
@@ -426,9 +632,8 @@ describe('buildFirmwareUpdatePlan', () => {
426
632
  'component:boot',
427
633
  'component:app_v1',
428
634
  'component:se01',
429
- 'resourceBundle:images',
430
635
  ]);
431
- expect(plan.targetsToUpdate).toEqual(['boot', 'app_v1', 'se01', 'resource']);
636
+ expect(plan.targetsToUpdate).toEqual(['boot', 'app_v1', 'se01']);
432
637
  expect(plan.artifacts[1]).toEqual(
433
638
  expect.objectContaining({
434
639
  expectedSize: 1024,
@@ -437,6 +642,41 @@ describe('buildFirmwareUpdatePlan', () => {
437
642
  );
438
643
  });
439
644
 
645
+ test.each([
646
+ {
647
+ expectedSize: undefined,
648
+ fingerprint: '1'.repeat(64),
649
+ },
650
+ {
651
+ expectedSize: 1024,
652
+ fingerprint: undefined,
653
+ },
654
+ ])('rejects a remote Pro2 component without complete integrity metadata', integrity => {
655
+ expectFirmwarePlanInvalid(
656
+ () =>
657
+ buildFirmwareUpdatePlan({
658
+ features: createFeatures({ deviceType: EDeviceType.Pro2 }),
659
+ firmwareType: EFirmwareType.Universal,
660
+ platform: 'desktop',
661
+ firmware: {
662
+ status: 'required',
663
+ release: {
664
+ components: {
665
+ applicationP1: {
666
+ target: 'APPLICATION_P1',
667
+ url: 'https://firmware.onekey.so/pro2/application-p1.bin',
668
+ ...integrity,
669
+ },
670
+ },
671
+ },
672
+ },
673
+ ble: noUpdate,
674
+ bootloader: noUpdate,
675
+ }),
676
+ 'integrity metadata is invalid'
677
+ );
678
+ });
679
+
440
680
  test('rejects Pro2 ROMLOADER because it requires a separate loader flow', () => {
441
681
  expect(() =>
442
682
  buildFirmwareUpdatePlan({
@@ -468,37 +708,19 @@ describe('buildFirmwareUpdatePlan', () => {
468
708
  application: {
469
709
  target: 'APPLICATION_P1',
470
710
  url: 'https://firmware.onekey.so/pro2/application.bin',
711
+ expectedSize: 1024,
712
+ fingerprint: '1111111111111111111111111111111111111111111111111111111111111111',
471
713
  },
472
714
  applicationDuplicate: {
473
715
  target: 'APPLICATION_P1',
474
716
  url: 'https://firmware.onekey.so/pro2/application-duplicate.bin',
717
+ expectedSize: 1024,
718
+ fingerprint: '2222222222222222222222222222222222222222222222222222222222222222',
475
719
  },
476
720
  },
477
721
  },
478
722
  expectedError: 'duplicates target app_v1',
479
723
  },
480
- {
481
- label: 'resource bundle name',
482
- release: {
483
- components: {
484
- application: {
485
- target: 'APPLICATION_P1',
486
- url: 'https://firmware.onekey.so/pro2/application.bin',
487
- },
488
- },
489
- resourceBundles: [
490
- {
491
- name: 'images',
492
- url: 'https://firmware.onekey.so/pro2/images.okpkg',
493
- },
494
- {
495
- name: 'images',
496
- url: 'https://firmware.onekey.so/pro2/images-duplicate.okpkg',
497
- },
498
- ],
499
- },
500
- expectedError: 'duplicates name images',
501
- },
502
724
  ])('rejects a duplicate Pro2 $label', ({ release, expectedError }) => {
503
725
  expect(() =>
504
726
  buildFirmwareUpdatePlan({
@@ -1,6 +1,9 @@
1
1
  import { EDeviceType, EFirmwareType } from '@onekeyfe/hd-shared';
2
2
 
3
- import { buildFirmwareUpdatePlan } from '../../src/api/firmware/FirmwareUpdatePlan';
3
+ import {
4
+ buildFirmwareUpdatePlan,
5
+ digestFirmwareUpdateContract,
6
+ } from '../../src/api/firmware/FirmwareUpdatePlan';
4
7
  import {
5
8
  assertFirmwareUpdatePreparedPlanBinding,
6
9
  assertFirmwareUpdatePreparedPlanDeviceIdentity,
@@ -209,6 +212,55 @@ describe('FirmwareUpdatePreparedPlan', () => {
209
212
  ).toThrow('entry name is invalid');
210
213
  });
211
214
 
215
+ test('rejects duplicate materialized entry names during standalone validation', () => {
216
+ const plan = buildPlan();
217
+ const prepared = prepareFirmwareUpdatePlan({
218
+ plan,
219
+ leaseRef: 'fwlease:00000000-0000-4000-8000-000000000001',
220
+ artifacts: [
221
+ {
222
+ artifactId: 'firmware',
223
+ artifact: artifact('a'.repeat(64), 4),
224
+ },
225
+ {
226
+ artifactId: 'resource',
227
+ artifact: artifact('b'.repeat(64), 8),
228
+ materializedEntries: [
229
+ {
230
+ entryName: 'resource/index.bin',
231
+ artifact: artifact('c'.repeat(64), 2),
232
+ },
233
+ ],
234
+ },
235
+ ],
236
+ });
237
+ const resourceArtifact = prepared.artifacts.find(item => item.target === 'resource')!;
238
+ const tamperedWithoutDigest = {
239
+ ...prepared,
240
+ artifacts: prepared.artifacts.map(item =>
241
+ item === resourceArtifact
242
+ ? {
243
+ ...item,
244
+ materializedEntries: [
245
+ ...resourceArtifact.materializedEntries!,
246
+ {
247
+ entryName: 'other/index.bin',
248
+ artifact: artifact('d'.repeat(64), 2),
249
+ },
250
+ ],
251
+ }
252
+ : item
253
+ ),
254
+ };
255
+ const { preparedPlanDigest: _preparedPlanDigest, ...digestInput } = tamperedWithoutDigest;
256
+ const tampered = {
257
+ ...tamperedWithoutDigest,
258
+ preparedPlanDigest: digestFirmwareUpdateContract(digestInput),
259
+ };
260
+
261
+ expect(() => validateFirmwareUpdatePreparedPlan(tampered)).toThrow('duplicate entry names');
262
+ });
263
+
212
264
  test('rejects unknown fields even when a caller presents a plan-shaped object', () => {
213
265
  const plan = buildPlan();
214
266
  expect(() =>