Page MenuHomePhabricator

[electrum] refactor private key class to better abstract ecdsa library usage
ClosedPublic

Authored by PiRK on Aug 23 2024, 16:14.

Details

Summary

The goal of this refactoring is to better encapsulate internals of the ecdsa library in a single module (in anticipation for removing that dependency later), and improve the general code quality.

The ECKey class is renamed into ECPrivkey and now inherits ECPubkey. The code of the ECPrivkey is massaged into a better shape. The constructor now checks that the key is exactly 32 bytes, and for creating a key from a shorter amount of data (used for wallet file encryption) a special classmethod from_arbitrary_size_secret is introduced with the previous behavior of normalizing the shorter key data to 32 bytes.

The MySigningKey class, which is a wrapper around ecdsa.SigningKey, is now an implementation details of ecc.py, and is no longer used in transaction.py and unit tests.

This diff is a big chunck of what remains to be split out of D16301.

Depends on D16678

Test Plan

python test_runner.py

Run the GUI, sign and verify a message, check that the sig is still identical to the one generated before this diff.
Sign, send and receive a transaction.

Diff Detail

Repository
rABC Bitcoin ABC
Branch
electrum_crypto
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 30073
Build 59680: Build Diffelectrum-tests
Build 59679: arc lint + arc unit

Event Timeline

PiRK edited the summary of this revision. (Show Details)

extracted the moving of .encrypt -> ECPubkey into its own "move-mostly" diff so that the changes are easier to spot in this one

PiRK edited the summary of this revision. (Show Details)

move the mostly unrelated fix in ECPubkey.encrypt_message to its own diff (D16678)

PiRK published this revision for review.Aug 26 2024, 06:42
PiRK planned changes to this revision.Aug 26 2024, 11:42

this silently breaks fusion

  File "~/bitcoin-abc/electrum/electrumabc_plugins/fusion/fusion.py", line 50, in <module>
    from electrumabc.ecc import public_key_from_private_key
ImportError: cannot import name 'public_key_from_private_key' from 'electrumabc.ecc' (~/bitcoin-abc/electrum/electrumabc/ecc.py)

I see this has been rebased but I am running into the same issue, arc patch 16676 fails with a cherry pick error

I can't get the test plan to pass but I'm also not sure it's really an issue.

Here's what I do.

  1. Open ElectrumABC on my desktop (I'm one minor version behind the latest)
  2. Sign message thisisatestmessage with address in wallet
  3. Signature generated
  4. Close electrumABC

Then open ElectrumABC from ./electrum-abc from patching this diff and repeat. I get a different signature. But the original signature also verifies.

Likewise I can verify the ./electrum-abc signature from this diff in my normal desktop ElectrumABC.

I can't get the test plan to pass but I'm also not sure it's really an issue.

It should not be an issue as long as the signature verifies (and this is reasonably well covered in the unit tests, with some deterministic hardcoded signatures), but I don't really understand how this change could affect the produces signature, and why it could change for you but not for me. So it is a bit odd.

confirmed my different signatures were from not having built libsecp

i think we can get rid of the 32 magic number since we have that as a constant now? other magic numbers are probably ok in this diff since these are already in the codebase, should be handled with a specific diff if the impact is warranted

electrum/electrumabc/ecc.py
356 ↗(On Diff #49406)

can we use the now-defined constant for this 32?

358 ↗(On Diff #49406)

same

374 ↗(On Diff #49406)

same

380 ↗(On Diff #49406)

not really clear to a dev coming across this what "legacy reasons" refers to, tho not sure if that is important beyond the explicit "don't use this for new things"

381 ↗(On Diff #49406)

magic 32

391 ↗(On Diff #49406)

magic 32

426 ↗(On Diff #49406)

why 4

looks like the previous code also has a lot of this stuff hardcoded so not really a blocker

This revision now requires changes to proceed.Aug 29 2024, 15:31
electrum/electrumabc/ecc.py
426 ↗(On Diff #49406)

Yeah it took me a while to figure this one out, and I'm not sure if it is worth a comment or a CONSTANT. This is only ever going to be in that signing function.
https://bitcoin.stackexchange.com/questions/38351/ecdsa-v-r-s-what-is-v/38909#38909

In Bitcoin, for message signatures, we use a trick called public key recovery. The fact is that if you have the full R point (not just its X coordinate) and s, and a message, you can compute for which public key this would be a valid signature. What this allows is to 'verify' a message with an address, without needing to know the full key (we just do public key recovery on the signature, and then hash the recovered key and compare it with the address).

However, this means we need the full R coordinates. There can be up to 4 different points with a given "X coordinate modulo n". (2 because each X coordinate has two possible Y coordinates, and 2 because r+n may still be a valid X coordinate). That number between 0 and 3 we call the recovery id, or recid.

replace a few 32's with PRIVATE_KEY_BYTECOUNT, clarify legacy use for arbitrary size secret

Fabien added a subscriber: Fabien.
Fabien added inline comments.
electrum/electrumabc/ecc.py
406 ↗(On Diff #49414)

You can make signature much faster by skipping this check, but let's do this in another diff as it's not new behavior

This revision is now accepted and ready to land.Sep 2 2024, 21:11