Skip to content

WIP: eliminate leak of node secret key from KeysInterface #1362

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

Conversation

devrandom
Copy link
Member

No description provided.

///
/// This method must return the same value each time it is called with a given `Recipient`
/// parameter.
fn get_node_secret(&self, recipient: Recipient) -> Result<SecretKey, ()>;
Copy link
Member Author

Choose a reason for hiding this comment

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

The goal is to eliminate this function.

@devrandom devrandom force-pushed the 2022-03-protocol-encryptor branch from 7697e7c to 3de8c72 Compare March 13, 2022 16:21
@devrandom devrandom force-pushed the 2022-03-protocol-encryptor branch from 3de8c72 to f7c4c6b Compare March 13, 2022 16:24
@codecov-commenter
Copy link

Codecov Report

Merging #1362 (7697e7c) into main (ca163c3) will decrease coverage by 0.02%.
The diff coverage is 88.55%.

❗ Current head 7697e7c differs from pull request most recent head f7c4c6b. Consider uploading reports for the commit f7c4c6b to get more accurate results

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1362      +/-   ##
==========================================
- Coverage   90.65%   90.63%   -0.03%     
==========================================
  Files          73       72       -1     
  Lines       40462    40539      +77     
==========================================
+ Hits        36682    36741      +59     
- Misses       3780     3798      +18     
Impacted Files Coverage Δ
lightning/src/ln/channel.rs 89.11% <0.00%> (-0.10%) ⬇️
lightning/src/ln/onion_route_tests.rs 97.62% <ø> (ø)
lightning/src/util/test_utils.rs 81.43% <66.66%> (-1.00%) ⬇️
lightning/src/chain/keysinterface.rs 93.18% <86.53%> (-0.40%) ⬇️
lightning-background-processor/src/lib.rs 93.10% <100.00%> (ø)
lightning-net-tokio/src/lib.rs 76.69% <100.00%> (ø)
lightning/src/ln/channelmanager.rs 84.76% <100.00%> (-0.02%) ⬇️
lightning/src/ln/peer_channel_encryptor.rs 95.00% <100.00%> (+0.01%) ⬆️
lightning/src/ln/peer_handler.rs 49.21% <100.00%> (+0.80%) ⬆️
... and 4 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ca163c3...f7c4c6b. Read the comment docs.

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Hmm, maybe best to just leave the private key in PeerManager for now as a first step and just address the ChannelManager parts? That would let us drop get_node_secret from KeysInterface, move it to KeysManager, and then we can address the desire for a ful shared secret there. I'm kinda meh on the general idea of a ECDH oracle in the signer interface, I'd really rather do "ECDH+get_rho_mu_from_shared_secret" (or um or ammag) all in one go instead to narrow the interface a bit.

/// A trait to describe an object which can get user secrets and key material.
pub trait KeysInterface {
/// A type which implements Sign which will be returned by get_channel_signer.
type Signer : Sign;

/// Get node secret key (aka node_id or network_key) based on the provided [`Recipient`].
/// A shared secret producer using the node key
fn get_shared_secret_producer(&self) -> Box<dyn SharedSecretProduce>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not quite sure I understand the need for this trait here, and super not a fan of box-dyn in our interface.

Copy link
Member Author

Choose a reason for hiding this comment

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

The trait replaces SecretKey in several places. It seems particularly necessary in DirectionalNoiseState, so that low-level protocol enum doesn't get exposed to the full KeysInterface.

The Box<dyn> was expedient for the draft - a generic would propagate into a lot of peer protocol structs.

@devrandom
Copy link
Member Author

devrandom commented Mar 14, 2022

Here are some thoughts:

  • ChannelManager first - that removes the accessor from KeysInterface, but the node implementer still needs to provide the secret key to PeerManager, so there's no security gain. That said, I agree it would be nice to split into two PRs.

  • narrow interface - if we really want to have a narrow interface, I would do onion encrypt/decrypt in the signer instead of ECDH or even mu/rho . For example, this would hide the preimage in keysends from the local node.

  • phantom node - perhaps instead of trying to fix all this now, an easier approach is to recommend that the implementer use a phantom node for invoicing, and again implement onion encrypt/decrypt in the signer. This would move the phantom node secret key into the signer and prevent bogus invoices from being issued.

@TheBlueMatt
Copy link
Collaborator

Hmm, doing onion decryption inside the signer seems fine, but I'm pretty dubious of hiding the contents in it such that the ChannelManager doesn't see the payment preimage for keysend and the like - getting the upgradability story right there sounds impossible. Also, having a general "here's an onion, please decrypt it once and give me the bytes back" oracle makes the phantom side of things trivial.

@TheBlueMatt
Copy link
Collaborator

That said, I'm not sure what thatwould provide that just returning the hashed shared secrets doesn't, except requiring copying more data...

@ariard
Copy link

ariard commented Mar 18, 2022

I'm still trying to grasp better the security gain of decrypting the onion on the signer side instead of the node. Like it could make sense for application where the onion payload is confidential content at destination of the node user herself but right now it's all processing data. Or maybe you're thinking about enforcing policies on the onion content itself before to yield it back to the local node ?

@devrandom
Copy link
Member Author

The main example that comes to mind is keysend. If the preimage leaks to a node other than the final recipient, the payment can be taken by an intermediate node.

@TheBlueMatt
Copy link
Collaborator

The main example that comes to mind is keysend. If the preimage leaks to a node other than the final recipient, the payment can be taken by an intermediate node.

Ugh, this just feels like a failure of the keysend design - lack of proof-of-payment sucks, yo :(. If there's no other issue with letting the ChannelManager see the decrypted onion, maybe we call it a shortcoming and move towards BOLT 12 as a brighter future? :)

Do you share the concern about upgradability? It seems kinda annoying to require a signer update to get any new onion TLV passed through, and I'd imagine c-lightning's current plugin archtecture assumes all the TLVs can be exposed.

@devrandom
Copy link
Member Author

devrandom commented Mar 22, 2022

The main example that comes to mind is keysend. If the preimage leaks to a node other than the final recipient, the payment can be taken by an intermediate node.

Ugh, this just feels like a failure of the keysend design - lack of proof-of-payment sucks, yo :(. If there's no other issue with letting the ChannelManager see the decrypted onion, maybe we call it a shortcoming and move towards BOLT 12 as a brighter future? :)

Sure.

Do you share the concern about upgradability? It seems kinda annoying to require a signer update to get any new onion TLV passed through, and I'd imagine c-lightning's current plugin archtecture assumes all the TLVs can be exposed.

Well, the signer could pass through anything it doesn't understand.

That said, I don't feel strongly about this, and passing onions through the signer does have a performance impact. So the ECDH-derived-key approach sounds good to me.

@TheBlueMatt
Copy link
Collaborator

Well, the signer could pass through anything it doesn't understand.

Hmm, true, of course that means new protocols default to "insecure" rather than "secure", which I guess is the key distinction. Anyway, we can revisit this later if keysend is still materially around in a few years.

@ariard
Copy link

ariard commented Mar 28, 2022

The main example that comes to mind is keysend. If the preimage leaks to a node other than the final recipient, the payment can be taken by an intermediate node.

Yes though moving onion decryption on the Signer side doesn't improve security in case of Node compromise, as the Node has to load in-memory the keysend preimage to fetch payment from counterparty ? If the payment path encryption worked well, your intermediate nodes won't learn the preimage, independently of where on the final host the decryption happens, I think.

Beyond, I lean to agree with Matt here, if we have payment/onion protocols in the future where it makes sense to minimize the trust towards the Node, we can rethink about it.

@devrandom
Copy link
Member Author

@ariard - my comment about keysend is about the security of the sender, not the recipient. The issue is if the preimage is leaked to an intermediate node, which then takes the funds from the sender without propagating the HTLC to the intended final recipient.

But anyway, I agree that it's not worth designing around keysend right now.

@ariard
Copy link

ariard commented Apr 1, 2022

@ariard - my comment about keysend is about the security of the sender, not the recipient. The issue is if the preimage is leaked to an intermediate node, which then takes the funds from the sender without propagating the HTLC to the intended final recipient.

@devrandom

Gotcha, you're correct that's a viable vector of keysend attack in case of Node compromise.

That lets me think, if I compromise your Node and I don't control your payees, can I feed you malicious payment paths, where all the intermediate nodes are mine and the routing fees are inflated. I see in policy-controls.md, there is the following requirement "The amount sent must not be greater than the amount in the invoice." but I'm not sure if it accounts routing fees ?

@devrandom
Copy link
Member Author

I see in policy-controls.md, there is the following requirement "The amount sent must not be greater than the amount in the invoice." but I'm not sure if it accounts routing fees ?

Yes, we do have a policy parameter max_routing_fee_msat which is the total maximum fee paid for routing for each payment. The text of the policy doesn't mention this, but it's there.

@TheBlueMatt
Copy link
Collaborator

Do you still intend to complete this, @devrandom?

@devrandom
Copy link
Member Author

yes, I do

@wpaulino
Copy link
Contributor

Didn't realize this existed. I opened #1951 to address it.

@TheBlueMatt
Copy link
Collaborator

Superseded by #1951.

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.

5 participants