secp256k1 icon indicating copy to clipboard operation
secp256k1 copied to clipboard

Add x-only ECDH support to ecdh module

Open sipa opened this issue 2 years ago • 24 comments

Built on top of #1118, this adds API calls to the ecdh module to support x-only ECDH. When pubkey decompression is included in the ECDH benchmark, it's roughly 10% faster to do x-only ECDH.

The default hash function included uses SHA256(shared_x_coordinate) as output function.

sipa avatar Jan 27 '23 18:01 sipa

Tried to fix the MSVC issues... let's see what CI says.

See commit messages. I verified locally that static linking doesn't break and just outputs a warning.

real-or-random avatar Jan 28 '23 09:01 real-or-random

Tried to fix the MSVC issues... let's see what CI says.

See commit messages. I verified locally that static linking doesn't break and just outputs a warning.

FWIW, the last commit fixes this branch build with MSVC when using CMake-based build system as well: https://cirrus-ci.com/build/6082649656131584

hebasto avatar Jan 28 '23 09:01 hebasto

@real-or-random Thanks for the fixes. I've removed my earlier attempt from the commit history. If this works, I'll extract the non-ECDH-related parts from this PR and submit them separately.

sipa avatar Jan 28 '23 17:01 sipa

We should probably move my two build/ci commits in a separate commit. At least the build commit fixes a real build issue that we simply haven't encountered so far, because none of our "user" binaries (bench+examples) have used variables exported from the variable so far (but just functions).

(I can take care of that, and I still want to rethink one detail here.)

real-or-random avatar Jan 28 '23 19:01 real-or-random

@real-or-random Yeah, I think both of your commits should be submitted as a separate PR. Perhaps the benchmark change here should also (as ECDH benchmarking without including pubkey decoding is misleading), so I was planning to do those 3. It won't be before tonight though, so if you want to, go ahead.

sipa avatar Jan 28 '23 19:01 sipa

@real-or-random Actually since both of the commits are yours, and perhaps you want to make some changes, perhaps you should open a PR for them?

I'll rebase this and/or open the ECDH benchmark change as a follow-up then.

sipa avatar Jan 29 '23 03:01 sipa

@real-or-random Actually since both of the commits are yours, and perhaps you want to make some changes, perhaps you should open a PR for them?

Yep, I'll take care of that!

real-or-random avatar Jan 30 '23 10:01 real-or-random

@real-or-random Ping?

Yep, I'll take care of that!

sipa avatar Feb 06 '23 15:02 sipa

@real-or-random Ping?

Yep, I'll take care of that!

Will do today or tomorrow!

real-or-random avatar Feb 06 '23 15:02 real-or-random

@real-or-random

Will do today or tomorrow!

Never mind, rebased.

sipa avatar Sep 19 '23 16:09 sipa

Concept ACK

Not entirely sure if there's demand for this, but we have it in the code base, and we have an ECDH module already, so I think it's pragmatic to expose this.

But this should hash both public keys into the shared secret. :)

real-or-random avatar Sep 20 '23 17:09 real-or-random

Concept ACK.

Stratum v2 uses x-only ECDH (but not ElligatorSwift), so this would be quite useful.

I'll try if I can get this PR to work with https://github.com/bitcoin/bitcoin/pull/28983

Sjors avatar Jan 05 '24 11:01 Sjors

@Sjors This reminds me to pick this up again. We agreed that the interface needed a rewrite to use the safer approach the ellswift module uses (with a default hasher that includes the two pubkeys). I'm sure that whatever Sv2 uses can be adapted to fit in that model.

sipa avatar Jan 05 '24 13:01 sipa

When naively performing ECDH with x-only keys, by just converting it to a compressed key that starts with 0x02, are the two sides supposed to get the same result?

ECDH(Alice_priv, Bob_pub_x) == ECDH(Bob_priv, Alice_pub_x)

Or only if Alice_priv and Bob_priv are chosen such that their public key has even Y, i.e. the compressed key starts with 0x02? Or is that not guaranteed at all?

Sjors avatar Jan 05 '24 13:01 Sjors

@Sjors That will work. In other words, negating the private key of one of the sides (or of both sides) won't change the resulting shared X coordinate. It will change the Y coordinate of the resulting shared point, but in x-only ECDH you normally ignore that Y coordinate.

sipa avatar Jan 05 '24 14:01 sipa

Mmm, but when using secp256k1_ecdh it does matter, doesn't it? Because the default ecdh_hash_function_sha256 uses the y-coordinate (version = (y32[31] & 0x01) | 0x02;).

Is that function based on any RFC or existing standard?

Sjors avatar Jan 05 '24 14:01 Sjors

@Sjors Not that I know of. What does Sv2 use?

sipa avatar Jan 05 '24 14:01 sipa

@sipa the spec is ambiguous on the ECDH details: https://github.com/stratum-mining/sv2-spec/blob/main/04-Protocol-Security.md#4311-ec-point-encoding-remarks

The Template Provider PR above uses ecdh_hash_function_sha256: https://github.com/bitcoin/bitcoin/pull/28983/commits/27928dba41bb0186bfe82e6668c0f1024f056772 (link to commit since I'm about to refactor this)

When you call that with a private key that has an odd corresponding public key, then both sides of the connection will generate a different output for CKey::ECDH(). That in turns breaks the cypher.

Sjors avatar Jan 05 '24 15:01 Sjors

@Sjors Heh, it's not specified how the shared point is serialized? That's worth addressing in the spec if possible (... or if it can still be changed, use ellswift?).

In any case, yes, if the full compressed point serialization of the shared point is used as secret, then you need to make sure the private key matches the (full) public key. That means it's not actually x-only ECDH, and this PR (or its envisioned successor) won't be usable, as it doesn't even compute the resulting shared Y coordinate.

sipa avatar Jan 05 '24 15:01 sipa

I think the spec can still be modified / clarified. It should probably only use the x-component of the shared point. That seems more consistent with the use of x-only public keys in the rest of the sv2 spec.

Sjors avatar Jan 05 '24 15:01 Sjors

@Sjors Is it an option to use ellswift negotiation instead? It's effectively a drop-in replacement for ECDH, except the public keys are 64 bytes instead of 32. Depending on how exactly Noise is being used to encode messages boundaries, that may actually be sufficient to make Sv2 have a pseudorandom bytestream.

sipa avatar Jan 05 '24 17:01 sipa

On the Bitcoin Core side that would be trivial to add support for. The Stratum Reference Implementation is written in Rust, is there already a library for that? Or I guess it would use libsecp indirectly anyway?

Sjors avatar Jan 06 '24 10:01 Sjors

@Sjors rust-secp256k1 appears to have ellswift bindings, but I don't know anything about Rust myself.

sipa avatar Jan 07 '24 14:01 sipa

There's pretty good progress in Stratum v2 on switching to EllSwift for both encoding the key on the wire and performing ECDH, so we probably won't need this.

Sjors avatar Feb 07 '24 08:02 Sjors