Skip to content

Expose EC point arithmetic and add bip352_encode for Silent Payments - #57

Open
macgyver13 wants to merge 4 commits into
switck:masterfrom
macgyver13:add-bip352-support
Open

Expose EC point arithmetic and add bip352_encode for Silent Payments#57
macgyver13 wants to merge 4 commits into
switck:masterfrom
macgyver13:add-bip352-support

Conversation

@macgyver13

Copy link
Copy Markdown

BIP-352 (Silent Payments) and BIP-374 (DLEQ proofs) need generic EC arithmetic
that the current API cannot express.

The existing keypair.ecdh_multiply() returns sha256(x || y) of the shared
point. BIP-352 needs the point, not a digest, because the ECDH share is
multiplied again by input_hash before it is hashed.

BIP-352 also uses point addition to sum input pubkeys into A_sum, combine per-input
ECDH shares, and add a label tweak to the spend key.

generator() and curve_order*() are exposed as constants because the Python
side references them on nearly every operation. Deriving G through
keypair(1).pubkey() per call is wasteful, and curve_order_int() skips an
int.from_bytes() on each scalar reduction.

bip352_encode follows BIP-352's bech32m + version-byte layout for scan_key || spend_key
(65 or 66 bytes depending on whether a scan pubkey or a watch-only scan privkey is passed).

Raises BECH32_MAX_LEN from bech32's 90-char cap to 128 to fit the silent payments address's
~117 chars, and re-derives the two pre-existing bech32 encode buffers from that same constant so
they don't drift.

Used by the Coldcard firmware's shared/silentpayments.py (BIP-352) and shared/dleq.py
(BIP-374) PR 587.

Note to reviewers:

cd external/libngu/ngu
../../../unix/coldcard-mpy ngu_tests/run.py
...
PASS - test_random
PASS - test_codecs_gen.py
PASS - test_hash_gen.py
PASS - test_hdnode
PASS - test_hdnode_gen.py
PASS - test_k1
PASS - test_k1_gen.py
PASS - test_bip39
PASS - test_bip32

@scgbckbone scgbckbone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • Replace vstr output and mp_obj_new_str_from_vstr(...) with a byte buffer returned via mp_obj_new_bytes()
uint8_t output[33];
size_t outlen = sizeof(output);
secp256k1_ec_pubkey_serialize(..., output, &outlen, ...);
return mp_obj_new_bytes(output, outlen);
  • Replace STATIC with standard lowercase static.

we will eventually migrate to newer mpy (v1.17 -> v1.28) and above is deprecated

Add thin wrappers returning unhashed curve points, which BIP-352 needs
because keypair.ecdh_multiply() returns sha256(x||y) and the ECDH share has
to be re-scaled by input_hash before it is hashed:
secp256k1_ec_pubkey_tweak_mul -> ec_pubkey_tweak_mul(pubkey, scalar32)
secp256k1_ec_pubkey_combine -> ec_pubkey_combine([pubkey, ...])

Add constants: generator(), curve_order() and curve_order_int()
Fold pairwise instead of allocating parse buffers for the whole list,
so memory use is constant regardless of input length.

Add test coverage for the new BIP-352 wrappers including the
accumulator's point-at-infinity handling.
Encodes scan || spend compressed pubkeys as bech32m per BIP-352, with an
optional version (0-31, default 0).

A v0 address is ~117 characters, past bech32's 90-char cap. bech32.patch now
names that cap BECH32_MAX_LEN (128) and derives BECH32_BUF_SIZE from it.

Also accept a 32-byte scan key for the watch-only spscan/tspscan export. That
payload is not a BIP-352 address and is only meaningful under those distinct HRPs.
Use lowercase static instead of STATIC.
Return byte buffer instead of vstr for s_ec_pubkey_tweak_mul and s_ec_pubkey_combine.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants