@@ -577,6 +577,20 @@ def patch(self, off, rel, data):
577577 with open (self .img , "r+b" ) as f :
578578 f .seek (off + rel )
579579 f .write (data )
580+ # These edits construct alternate-version fixtures, not corrupt
581+ # records. Preserve the optional durable-commit envelope so boot
582+ # reaches the version reader rather than rejecting a stale CRC.
583+ f .seek (off )
584+ record = f .read (2580 )
585+ if record [2572 :2576 ] == b"crc1" :
586+ crc = 0xffffffff
587+ for (word ,) in struct .iter_unpack ("<I" , record [:2572 ]):
588+ crc ^= word
589+ for _ in range (32 ):
590+ crc = ((crc << 1 ) ^ (0x04c11db7 if crc & 0x80000000
591+ else 0 )) & 0xffffffff
592+ f .seek (off + 2576 )
593+ f .write (struct .pack ("<I" , crc ))
580594 f .flush ()
581595 os .fsync (f .fileno ())
582596
@@ -1110,8 +1124,18 @@ def test_v16_blob_upgrades_without_wiping(self):
11101124 derive if the wrapped storage key unwrapped, the 512-byte V16
11111125 ciphertext decrypted, and the seed came back byte-identical.
11121126 """
1127+ self ._check_v16_upgrade ()
1128+
1129+ def test_unframed_v16_blob_upgrades_without_wiping (self ):
1130+ """Legacy V16 without the optional CRC envelope preserves its wallet."""
1131+ self ._check_v16_upgrade (unframed = True )
1132+
1133+ def _check_v16_upgrade (self , unframed = False ):
11131134 addr , off = self ._create_wallet ()
11141135 self ._make_v16_blob (off )
1136+ if unframed :
1137+ self .emu .patch (off , 41 , b"\x00 " * 3 )
1138+ self .emu .patch (off , 2572 , b"\xff " * 8 )
11151139 self .assertEqual (16 , self .emu .read_u32 (off , OFF_VERSION ))
11161140
11171141 before = self .emu .image ()
@@ -1145,7 +1169,7 @@ def test_v16_blob_upgrades_without_wiping(self):
11451169 c .close ()
11461170
11471171 def test_unrecognised_version_wipes_on_boot (self ):
1148- """A downgrade wipes, deliberately -- do not "fix" this .
1172+ """Unknown full-product versions wipe; Bitcoin-only bands stay intact .
11491173
11501174 A device that has run newer firmware carries a newer stamp. Older
11511175 firmware cannot read it, so version_from_int() returns
@@ -1161,6 +1185,7 @@ def test_unrecognised_version_wipes_on_boot(self):
11611185 addr , off = self ._create_wallet ()
11621186 unknown = self .emu .read_u32 (off , OFF_VERSION ) + 1
11631187 self .emu .write_u32 (off , OFF_VERSION , unknown )
1188+ before = self .emu .image ()
11641189
11651190 self .emu .boot ()
11661191 c = self .emu .client (self .method )
@@ -1174,6 +1199,12 @@ def test_unrecognised_version_wipes_on_boot(self):
11741199 "an older signed image would keep the seed." % unknown )
11751200 self .assertFalse (c .features .pin_protection )
11761201 self .assertNotEqual (LABEL , c .features .label )
1202+ # Unknown versions in the Bitcoin-only band are refused without
1203+ # erasing the wallet; full firmware uses its existing wipe policy.
1204+ if self .bitcoin_only :
1205+ self .assertEqual (before , self .emu .image ())
1206+ else :
1207+ self .assertNotEqual (before , self .emu .image ())
11771208 finally :
11781209 c .close ()
11791210
@@ -1215,10 +1246,10 @@ def test_bitcoin_only_band_refuses_without_wiping(self):
12151246 self .emu .read_u32 (off , OFF_VERSION ), STORAGE_VERSION_BTC_ONLY_BASE ,
12161247 "this emulator already stamps its wallets into the bitcoin-only "
12171248 "band, so it is not the multi-chain firmware this test is about" )
1218- before = self .emu .sector (off )
12191249 self .emu .write_u32 (
12201250 off , OFF_VERSION ,
12211251 STORAGE_VERSION_BTC_ONLY_BASE + self .emu .read_u32 (off , OFF_VERSION ))
1252+ before = self .emu .image ()
12221253
12231254 self .emu .boot ()
12241255 c = self .emu .client (self .method )
@@ -1233,10 +1264,9 @@ def test_bitcoin_only_band_refuses_without_wiping(self):
12331264 c .close ()
12341265 self .emu .halt ()
12351266
1236- after = self .emu .sector ( off )
1267+ after = self .emu .image ( )
12371268 self .assertEqual (
1238- before [:OFF_VERSION ] + before [OFF_VERSION + 4 :],
1239- after [:OFF_VERSION ] + after [OFF_VERSION + 4 :],
1269+ before , after ,
12401270 "the locked boot MODIFIED the bitcoin-only record. The wallet is "
12411271 "supposed to stay recoverable by reflashing bitcoin-only firmware" )
12421272
0 commit comments