Skip to content

Authenticate blinded path contexts with a secret AAD in the MAC #3845

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

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

TheBlueMatt
Copy link
Collaborator

When we receive an onion message, we often want to make sure it was
sent through a blinded path we constructed. This protects us from
various deanonymization attacks where someone can send a message to
every node on the network until they find us, effectively
unwrapping the blinded path and identifying its recipient.

We generally do so by adding authentication tags to our
`MessageContext` variants. Because the contexts themselves are
encrypted (and MAC'd) to us, we only have to ensure that they
cannot be forged, which is trivially accomplished with a simple
nonce and a MAC covering it.

This logic has ended up being repeated in nearly all of our onion
message handlers, and has gotten quite repetitive.

Instead, here, we simply authenticate the blinded path contexts
using the MAC that's already there, but tweaking it with an
additional secret as the AAD in Poly1305. This prevents forgery as
the secret is now required to make the MAC check pass.

Ultimately this means that no one can ever build a blinded path
which terminates at an LDK node that we'll accept, but over time
we've come to recognize this as a useful property, rather than
something to fight. Here we finally break from the spec fully in
our context encryption (not just the contents thereof).

This will save a bit of space in some of our `MessageContext`s,
though sadly not in the blinded path we include in `Bolt12Offer`s,
so they're generally not in space-sensitive blinded paths.

We can apply the same logic in our blinded payment paths as well,
but we do not do so here.

This is basically done, the only step left is to change the public API to take in a new key, rather than using the constant [41; 32]. Someone with an LLM agent setup properly can probably make it do the work.

@ldk-reviews-bot
Copy link

👋 Hi! I see this is a draft PR.
I'll wait to assign reviewers until you mark it as ready for review.
Just convert it out of draft status when you're ready for review!

`Poly1305::raw_result` copies the output into a slice, for some
reason allowing any length sice. This isn't a great API, so while
we're here we change it to return the 16-byte tag instead.
Rather than skipping compilation of `poly1305.rs` when building for
fuzzing and relying on `ChaCha20Poly1305` to do the fuzzing
variants, implement an actual fuzz wrapper in `poly1305.rs`,
keeping the same fuzz MAC structure that we already have.

We also add a fuzzing implementation of `fixed_time_eq` which does
a simple comparison, to allow the fuzzer to "see into" the
comparison in some cases.

Best reviewed with `-b`.
@TheBlueMatt TheBlueMatt force-pushed the 2025-06-internally-auth-contexts branch from 1d3985b to 952ea78 Compare June 26, 2025 12:38
`ChaChaPolyReadAdapter` decodes an arbitrary object and checks the
poly1305 tag. In the coming commits, we'll need a variant of this
which allows for an *optional* AAD in the poly1305 tag, accepting
either tag as valid, but indicating to the caller whether the AAD
was used.

We could use the actual AAD setup in poly1305, which puts the AAD
first in the MAC (and then pads it out to a multiple of 16 bytes),
but since we're gonna check both with and without, its nice to
instead put the AAD at the end, enabling us to only calculate most
of the hash once before cloning its state and adding the AAD block.

We do this by swapping the AAD and the data being MAC'd in the
AAD-containing MAC check (but leaving them where they belong for
the non-AAD-containing MAC check).

We also add a corresponding `chachapoly_encrypt_with_swapped_aad`
which allows encrypting with the new MAC format.
@TheBlueMatt TheBlueMatt force-pushed the 2025-06-internally-auth-contexts branch 2 times, most recently from 56b3d22 to d4a4b6e Compare June 26, 2025 13:20
@TheBlueMatt
Copy link
Collaborator Author

Okay, I tweaked this PR so that it can authenticate forwarding hops in blinded paths. It still needs someone (or an LLM?) to pick it up and pipe the auth key through from the blinded path builders to the crypto routines, then we can go drop most of the context validation in existing onion message handling, but I think the crypto part works.

@valentinewallace
Copy link
Contributor

valentinewallace commented Jun 26, 2025

I'm not sure on the exact PR order yet, but will try to pick this up soon to unblock async payments since we already removed the preexisting validation scheme usage in #3618.

Edit: this is no longer blocking async payments, will just re-add the to-be-deprecated validation fields to the relevant async payments messages if needed

When we receive an onion message, we often want to make sure it was
sent through a blinded path we constructed. This protects us from
various deanonymization attacks where someone can send a message to
every node on the network until they find us, effectively
unwrapping the blinded path and identifying its recipient.

We generally do so by adding authentication tags to our
`MessageContext` variants. Because the contexts themselves are
encrypted (and MAC'd) to us, we only have to ensure that they
cannot be forged, which is trivially accomplished with a simple
nonce and a MAC covering it.

This logic has ended up being repeated in nearly all of our onion
message handlers, and has gotten quite repetitive.

Instead, here, we simply authenticate the blinded path contexts
using the MAC that's already there, but tweaking it with an
additional secret as the AAD in Poly1305. This prevents forgery as
the secret is now required to make the MAC check pass.

Ultimately this means that no one can ever build a blinded path
which terminates at an LDK node that we'll accept, but over time
we've come to recognize this as a useful property, rather than
something to fight. Here we finally break from the spec fully in
our context encryption (not just the contents thereof).

This will save a bit of space in some of our `MessageContext`s,
though sadly not in the blinded path we include in `Bolt12Offer`s,
so they're generally not in space-sensitive blinded paths.

We can apply the same logic in our blinded payment paths as well,
but we do not do so here.

This commit only adds the required changes to the cryptography, for
now it uses a constant key of `[41; 32]`.
In the previous commit we added support for authenticating received
blinded paths by using an additional secret as the AAD in the MAC.

Here, we extend this to support authenticating blinded path
contexts received for forwarding, allowing us to authenticate dummy
hops added as padding. This will allow us to prevent a DoS attack
where someone could create a blinded path which has many forwarding
hops all for us as fictitious dummy hops, requiring us to decrypt
many times only to find no useful onion message.
@TheBlueMatt TheBlueMatt force-pushed the 2025-06-internally-auth-contexts branch from d4a4b6e to ad214bc Compare June 26, 2025 15:12
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.

3 participants