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

multi: Route blinding #57

Merged
merged 9 commits into from
Aug 23, 2023
Merged

Conversation

ellemouton
Copy link
Contributor

This PR adds the changes that higher layers will need in order to take advantage of route blinding as defined in the spec proposal

Note that callers of this code will need to know how to extract the next_blinding_point (if the caller is the introduction node in the route) and next_blinding_override points from the onion packet payloads.

Copy link

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

very high level pass, just submitting in case GH loses my comments

blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
crypto.go Outdated Show resolved Hide resolved
crypto.go Outdated Show resolved Hide resolved
crypto.go Outdated Show resolved Hide resolved
@carlaKC
Copy link

carlaKC commented Jul 5, 2022

Also a meta thing that I noticed while using this PR. When this is merged I think that it would make sense to make two tags: one on the blinded route commit, and one on the final merge/full PR.

Reason being that code that imports this change and lnd requires lnd to be upgraded to use the new arg in ProcessOnionPacket (which requires lnd to update its dependency as well). Adding an extra tag to easily allow code to just point to the blinded paths stuff could be useful in avoiding modhell/ requiring lnd to update its dep prematurely. Just a note for future-selves.

Copy link

@sputn1ck sputn1ck left a comment

Choose a reason for hiding this comment

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

Very cool PR. Have not tested it yet and want to compare it to other implementations.

blinding.go Outdated Show resolved Hide resolved
blinding_test.go Outdated Show resolved Hide resolved
Copy link

@calvinrzachman calvinrzachman left a comment

Choose a reason for hiding this comment

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

This PR makes really good use of the existing functionality, nicely leveraging the similarities between route blinding and normal sphinx processing. Props for not getting lost in the “blinding” sauce (route blinding, blinding factor, blinding point, etc.)! I also like how the implementation shields any complexity of the route blinding protocol from callers. They just proceed like normal and leave the nuances of decrypting onions in a blinded route to the library. I think the effort there will help with integrating this into the Channel Link.

A few of my comments are notes. Let me know if our understandings match and feel free to resolve them so they don’t add clutter! My favorite PR - only to be superseded by the PR which adds this to LND :) Thanks for working on this!

blinding.go Outdated Show resolved Hide resolved
crypto.go Outdated
var blindingFactor btcec.ModNScalar
blindingFactor.SetBytes(&blindingFactorBytes)

ephemeral := blindGroupElement(dhKey, blindingFactor)

Choose a reason for hiding this comment

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

Subtle Note: Rather than directly computing the blinded private key, b(i), we follow this section of the route blinding proposal document. This is ephemeral = blinding_factor*Esender

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes indeed :) i have added a comment to point this out

Choose a reason for hiding this comment

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

Nit: I haven't seen this definition in the spec, especially the context you are usingblinding_factor here @calvinrzachman ?

Choose a reason for hiding this comment

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

Apologies for any confusion, Ziggie. It's been a while since I've looked at this, but I'm pretty sure that when I say blinding_factor here I am referring to Elle's variable blindingFactor := HMAC256("blinded_node_id", ss(i)). Which you are correct does differ from how the term is used in the spec document. Brought that up with Elle briefly here

Also, looks like my link may have gotten rug pulled by a change to t-bast's underlying spec PR. I usually pick an older, non-master release or tag so that links point to the expected line, but I don't know how to do that for an actively changing PR :/ In any case, the section this used to link to is now here.

crypto.go Outdated
blindingFactor.SetBytes(&blindingFactorBytes)

ephemeral := blindGroupElement(dhKey, blindingFactor)
return sharedSecret(r.onionKey, ephemeral)

Choose a reason for hiding this comment

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

This step mixes the blinding factor and node ID private key, k(i), implicitly computing the blinded node ID private key, b(i), and is equivalent to using b(i) in an ECDH with the sender's usual ephemeral key from the onion, Esender.

ECDH(b(i), Esender) is not done explicitly. Rather we have ECDH(k(i), blinding_factor*Esender), which correctly returns a shared secret between onion encryptor and routing node:

sssender/node = k*blinding_factor*Esender = (blinding_factor*k)*Esender = b(i)*Esender = ECDH(b(i), Esender)

From here the caller is free to use rho = HMAC-256("rho", sssender/node) to decrypt the onion like normal.

Choose a reason for hiding this comment

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

In the line for ss_sender/node you're missing hash functions, thanks for the comment, this helped me 🙏.

Choose a reason for hiding this comment

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

Thanks so much, very useful comment, we need this definitely in the codebase too.

Choose a reason for hiding this comment

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

In the line for ss_sender/node you're missing hash functions, ...

You are quite right. Glad the message still came through!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

cool - added some more comments to help describe this

blinding.go Outdated Show resolved Hide resolved
Copy link
Contributor Author

@ellemouton ellemouton left a comment

Choose a reason for hiding this comment

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

Thanks for the reviews y'all!! updated 🔥

blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
blinding_test.go Outdated Show resolved Hide resolved
crypto.go Outdated Show resolved Hide resolved
crypto.go Outdated Show resolved Hide resolved
crypto.go Outdated
var blindingFactor btcec.ModNScalar
blindingFactor.SetBytes(&blindingFactorBytes)

ephemeral := blindGroupElement(dhKey, blindingFactor)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes indeed :) i have added a comment to point this out

@ellemouton
Copy link
Contributor Author

making a note here for myself to update the PR to point to the latest state of the spec PR & its test vectors

@ellemouton
Copy link
Contributor Author

ah ok, looks like the updated test vectors still pass with the existing code. I think the main changes were related to the tlv fields/types which does not affect this pr since pr assumes the tlvs are already encoded. Will double check this later though

@lightninglabs-deploy
Copy link

@ellemouton, remember to re-request review from reviewers when ready

@ellemouton
Copy link
Contributor Author

!lightninglabs-deploy mute

blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
obfuscation.go Outdated Show resolved Hide resolved
Copy link

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

LGTM! Just needs rebase on ca23184 - when I import this to LND now some tests fail.

I've used this for onion messagaing and have interop with CL + LDK, and everything works as advertized in a WIP branch for forwarding blinded routes for LND.

blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
@carlaKC
Copy link

carlaKC commented Jan 25, 2023

Just needs rebase on ca23184 - when I import this to LND now some tests fail.

Rebase-beg when you have a minute!

@ellemouton
Copy link
Contributor Author

done! cc @carlaKC

Copy link

@bitromortac bitromortac left a comment

Choose a reason for hiding this comment

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

Cool work, this looks really nice 👍. Looking forward to explore the usage of this fundamental implementation 🎉

cmd/main.go Outdated Show resolved Hide resolved
cmd/main.go Outdated Show resolved Hide resolved
cmd/main.go Outdated Show resolved Hide resolved
cmd/main.go Outdated Show resolved Hide resolved
testdata/route-blinding-test.json Show resolved Hide resolved
blinding_test.go Outdated Show resolved Hide resolved
blinding_test.go Outdated Show resolved Hide resolved
blinding_test.go Outdated Show resolved Hide resolved
sphinx.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
@ellemouton ellemouton force-pushed the routeBlinding branch 4 times, most recently from f3212d5 to f050833 Compare May 10, 2023 09:44
@ellemouton ellemouton requested a review from bitromortac May 16, 2023 17:06
Copy link

@ziggie1984 ziggie1984 left a comment

Choose a reason for hiding this comment

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

Impressive work ⚡️ :)
Took me a a bit to go through and understand the big picture here 😅, especially the blinding variables where tricky for me, so I left some question regarding the whole topic, apart from that I had only some nits.

Keep up the very good work 🚀

blinding.go Outdated
BlindedHops []*BlindedHopInfo
}

// hopInfo represents a single hop in a blinded path. The BlindedHopInfo wrapper

Choose a reason for hiding this comment

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

Q: should this note be the other way around, shouldn't we use the BlindedHopInfo wrapper for hops with the blinded nodeId?

blinding.go Outdated
// point that the introduction node will use in order to create a shared
// secret with the builder of the blinded route. This point will need
// to be communicated to the introduction node by the sender in some
// way.

Choose a reason for hiding this comment

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

Q: What are the ways how this introduction point could be delivered to the First node in the blinded path? Do you refer in this comment to concatenate blinded paths for example ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What are the ways how this introduction point could be delivered to the First node in the blinded path?

it will be communicated via update_add_htlc

Do you refer in this comment to concatenate blinded paths for example ?

not sure what you mean?

crypto.go Show resolved Hide resolved
crypto.go Outdated
var blindingFactor btcec.ModNScalar
blindingFactor.SetBytes(&blindingFactorBytes)

ephemeral := blindGroupElement(dhKey, blindingFactor)

Choose a reason for hiding this comment

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

Nit: I haven't seen this definition in the spec, especially the context you are usingblinding_factor here @calvinrzachman ?

crypto.go Outdated
blindingFactor.SetBytes(&blindingFactorBytes)

ephemeral := blindGroupElement(dhKey, blindingFactor)
return sharedSecret(r.onionKey, ephemeral)

Choose a reason for hiding this comment

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

Thanks so much, very useful comment, we need this definitely in the codebase too.

blinding.go Outdated
@@ -0,0 +1,169 @@
package sphinx

Choose a reason for hiding this comment

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

Nit: Update Commit BlindBlindedRoute => BuildBlindedRoute

Choose a reason for hiding this comment

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

DecryptBlindedData would need an update as well

blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated Show resolved Hide resolved
blinding.go Outdated
return nil, err
}

blindingFactor := computeBlindingFactor(ephemPub, ss[:])

Choose a reason for hiding this comment

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

Hmm, so we have blindingFactor but also blindingFactorBytes (in this PR). The first one adheres with the definition in the specification the other one not. I agree both are blinding scalars but this confused me, maybe adding some comments where we diverge ?

blinding_test.go Outdated
pathED, err := BuildBlindedPath(eveSessKey, eveDavePath)
require.NoError(t, err)

// At this point, Eve will give her blinded path to Bob who will then

Choose a reason for hiding this comment

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

Q: I do not quite understand what sense it makes for Bob to use a blinded Path to Carol when it's only one Hop from Bob => Carol, does it make sense in the real world?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no it probably doesnt make sense in the real world. This is just to mimic the scenario in the spec test vector

Copy link

@bitromortac bitromortac left a comment

Choose a reason for hiding this comment

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

LGTM 🔥 🚀 again, very nice work!

cmd/main.go Outdated Show resolved Hide resolved
blinding.go Outdated
@@ -0,0 +1,169 @@
package sphinx

Choose a reason for hiding this comment

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

DecryptBlindedData would need an update as well

blinding_test.go Outdated Show resolved Hide resolved
cmd/main.go Show resolved Hide resolved
Update the TestVariablePayloadOnion test to make use of the `require`
package.
Add a TestTLVPayloadOnion test that tests the newer spec test vector
which tests the construction of an onion in the case where all the hops
are using the TLV version payload.
This commit updates the cli tool to use the urfave library. This makes
things easier to read and will make it easier to extend the tool in
future. A `genKeys` function is also added as a helper for quickly
generating key pairs.
This commit is a pure refactor and move commit. It moves the HopPayload
methods out from the `path.go` file and into a dedicated `payload.go`
file in order to prepare for a follow up commit that will add a blinded
path struct to the `path.go` file. The opportunity is also taken to
separate out the tlv vs legacy encoding of the hop payload a bit
(seprate constructors for example) in order to prepare for a future
where the legacy encoding will be removed.
crypto.go Outdated Show resolved Hide resolved
crypto.go Outdated Show resolved Hide resolved
crypto.go Outdated
}

// Encrypt the message and append the ciphertext to the nonce.
return aead.Seal(plainTxt[:0], chaChaPolyZeroNonce, plainTxt, nil), nil
Copy link
Member

Choose a reason for hiding this comment

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

Is plainTxt actually always appropriately sized? IIUC this is only useful when you have a buffer that is already pre-allocated, as then you end up with plaintext || ciphertext after the routine ends. In that case, you don't even need the return value though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the comment for Seal says:

// To reuse plaintext's storage for the encrypted output, use plaintext[:0]
// as dst. Otherwise, the remaining capacity of dst must not overlap plaintext.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

so kinda just went with that suggestion

path.go Outdated Show resolved Hide resolved
path.go Outdated Show resolved Hide resolved
crypto.go Show resolved Hide resolved
crypto.go Show resolved Hide resolved
obfuscation.go Outdated Show resolved Hide resolved
sphinx.go Outdated Show resolved Hide resolved
sphinx.go Show resolved Hide resolved
This commit introduces a new `BlindedPath` struct which holds all the
information defining a blinded route. The commit also includes the
addition of various helper methods to blind and unblind node IDs and
hop data. Test vectors from the spec are also added.
In this commit, ProcessOnionPacket is updated to take in a blinding key
as a parameter. This key is then used in order to determine the blinding
factor necessary to decrypt the onion. This change is accompanied with a
test that tests this change against a test vector from the route
blinding spec PR.
In this commit, DecryptBlindHopData and NextEphemeral methods are added
to Router which makes use of the Router's onion key in order to unblind
blinded data and compute the next ephermal key respectively.
Add a blinded-key option to the parse command so that it can be used to
test parsing of an onion for hops in a blinded route. Also add a helper
nextephemeral command.
Copy link
Contributor Author

@ellemouton ellemouton left a comment

Choose a reason for hiding this comment

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

Thanks for the review @Roasbeef 🙏 ready for another round!

crypto.go Outdated Show resolved Hide resolved
crypto.go Outdated
}

// Encrypt the message and append the ciphertext to the nonce.
return aead.Seal(plainTxt[:0], chaChaPolyZeroNonce, plainTxt, nil), nil
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the comment for Seal says:

// To reuse plaintext's storage for the encrypted output, use plaintext[:0]
// as dst. Otherwise, the remaining capacity of dst must not overlap plaintext.

crypto.go Outdated Show resolved Hide resolved
path.go Outdated Show resolved Hide resolved
testdata/route-blinding-test.json Show resolved Hide resolved
path.go Outdated Show resolved Hide resolved
crypto.go Show resolved Hide resolved
path.go Outdated Show resolved Hide resolved
path.go Outdated Show resolved Hide resolved
crypto.go Outdated
}

// Encrypt the message and append the ciphertext to the nonce.
return aead.Seal(plainTxt[:0], chaChaPolyZeroNonce, plainTxt, nil), nil
Copy link
Contributor Author

Choose a reason for hiding this comment

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

so kinda just went with that suggestion

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

LGTM ⛷️

@Roasbeef Roasbeef merged commit 06182b1 into lightningnetwork:master Aug 23, 2023
@ellemouton ellemouton deleted the routeBlinding branch August 23, 2023 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: High Priority
Status: Done
Development

Successfully merging this pull request may close these issues.

8 participants