Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multisig fix in legacy #4396

Merged
merged 6 commits into from
Jan 28, 2025
Merged

Multisig fix in legacy #4396

merged 6 commits into from
Jan 28, 2025

Conversation

onvej-sl
Copy link
Contributor

This pull request backports #4351 to legacy.

@onvej-sl onvej-sl added the T1B1 legacy Trezor One label Nov 27, 2024
@onvej-sl onvej-sl force-pushed the onvej-sl/multisig-fix-legacy branch 2 times, most recently from 4839e37 to c9adb55 Compare December 4, 2024 15:28
@onvej-sl onvej-sl marked this pull request as ready for review December 4, 2024 16:05
@onvej-sl onvej-sl requested review from andrewkozlik and mmilata and removed request for prusnak and matejcik December 4, 2024 16:06
@andrewkozlik andrewkozlik removed their request for review January 22, 2025 12:19
legacy/firmware/fsm_msg_coin.h Outdated Show resolved Hide resolved
legacy/firmware/fsm_msg_coin.h Outdated Show resolved Hide resolved
Copy link

github-actions bot commented Jan 24, 2025

core UI changes device test click test persistence test
T2T1 Model T test(screens) main(screens) test(screens) main(screens) test(screens) main(screens)
T3B1 Safe 3 test(screens) main(screens) test(screens) main(screens) test(screens) main(screens)
T3T1 Safe 5 test(screens) main(screens) test(screens) main(screens) test(screens) main(screens)
All main(screens)

Copy link
Member

@mmilata mmilata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • fixtures are missing (tests/update_fixtures.py ci should work)
  • I'm getting some failures on hardware that I'm not seeing in emulator, e.g.:
    FAILED tests/device_tests/bitcoin/test_multisig_change.py::test_sorted_multisig_change_match_first - AssertionError: Expected responses:
    FAILED tests/device_tests/bitcoin/test_multisig_change.py::test_multisig_change_match_second - AssertionError: Expected responses:
    FAILED tests/device_tests/bitcoin/test_multisig_change.py::test_multisig_change_match_first - AssertionError: Expected responses:
    FAILED tests/device_tests/bitcoin/test_bcash.py::test_send_bch_multisig_change - AssertionError: Expected responses:
    
    Not 100% sure this is because of this PR, would appreciate someone else checking. Needs fix(legacy): uint64 protobuf field decoding #4535 in order to work on HW.

@onvej-sl
Copy link
Contributor Author

I'm getting some failures on hardware that I'm not seeing in emulator, e.g.:

I hopefully fixed it in bc0ecfe.

@onvej-sl onvej-sl force-pushed the onvej-sl/multisig-fix-legacy branch from bc0ecfe to bedff6e Compare January 27, 2025 20:12
@onvej-sl
Copy link
Contributor Author

I squashed the fixup commits, rebased on top of the main branch and added fixtures.

@onvej-sl
Copy link
Contributor Author

I'm getting some failures on hardware that I'm not seeing in emulator, e.g.:

I hopefully fixed it in bc0ecfe.

My trezor one device is somewhat broken so I was only able to run tests/device_tests/bitcoin/test_multisig_change.py and tests/device_tests/bitcoin/test_bcash.py and both ran successfully.

@mmilata
Copy link
Member

mmilata commented Jan 27, 2025

Can confirm that all multisig tests now pass on my T1. But I don't understand where the problem was - shouldn't multisig->pubkeys_order be all zeros when not present in the message? Is this related to -fshort-enums?

@onvej-sl
Copy link
Contributor Author

onvej-sl commented Jan 27, 2025

To be honest, I'm not certain. However, based on how the code is carefully written (see, for example, this), I thought that property is uninitialized if has_property is false.

EDITED: Changed example.

@prusnak
Copy link
Member

prusnak commented Jan 27, 2025

I think the property is set to zeroes if unset by wire, but still I prefer to have the code explicit rather than implicit to be on the safe side once this behaviour changes in future versions of nanopb or something like that.

Is this related to -fshort-enums?

Also this might be the culprit in some cases like we've seen recently. So explicit code is better.

@mmilata
Copy link
Member

mmilata commented Jan 27, 2025

Yep I think it's -fshort-enums, on hardware sizeof(MultisigPubkeysOrder) == 1 so sha256_Update((const uint8_t *)&(multisig->pubkeys_order), sizeof(uint32_t)) reads also the next member in the struct, and it's not 0. Emulator doesn't use that option so it works there.

SHA256_CTX ctx = {0};
sha256_Init(&ctx);
sha256_Update(&ctx, (const uint8_t *)&(multisig->m), sizeof(uint32_t));
sha256_Update(&ctx, (const uint8_t *)&(pubkeys_order), sizeof(uint32_t));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...that means that this code is wrong, right?
what we want is, pseudocode:

MultisigPubkeysOrder pubkeys_order = ...
sha256_Update(multisig->m, sizeof(multisig->m);
sha256_Update(pubkeys_order, sizeof(pubkeys_order));
...

etc for this whole block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the code is correct now. The only difference from your pseudocode is that I cast MultisigPubkeysOrder to uint32_t.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my point is that we're magically relying on all the things being u32-sized, with no actual proof in the code (which was the source of the previous bug)

the code is correct now, but could break again in theory

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no problem changing uint32_t to MultisigPubkeysOrder. However, I don't still understand what you mean by "but could break again in theory". My code doesn't rely on MultisigPubkeysOrder to be uint32_t.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, it is not important, but generally, I think my solution is better because it ensures that the resulting hash is the same on both the emulator and the hardware.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both the requirement that the hash result stays stable as well as resilience to member size changes are valid.

From this POV it would be best to go with

sha256_Update(multisig->m, sizeof(multisig->m));

followed by a static assert that sizeof(multisig->m) == sizeof(uint32_t). As it's a matter of taste I'd say let's merge this PR as it is so we don't hold up the release, and continue in another PR.

Copy link
Contributor Author

@onvej-sl onvej-sl Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I understand now. I initially thought you were only referring to pubkeys_order, but you are also talking about m and others.

@onvej-sl
Copy link
Contributor Author

I tested it and can confirm that the size of MultisigPubkeysOrder is 4 in the emulator, while it is 1 on the hardware.

@onvej-sl onvej-sl merged commit 5f1dd83 into main Jan 28, 2025
94 checks passed
@onvej-sl onvej-sl deleted the onvej-sl/multisig-fix-legacy branch January 28, 2025 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T1B1 legacy Trezor One
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants