Skip to content

Commit 96cdae0

Browse files
committed
Add a MessageContext::DNSResolution to protect against probing
When we make a DNSSEC query with a reply path, we don't want to allow the DNS resolver to attempt to respond to various nodes to try to detect (through timining or other analysis) whether we were the one who made the query. Thus, we need to include a nonce in the context in our reply path, which we set up here by creating a new context type for DNS resolutions.
1 parent 4373343 commit 96cdae0

File tree

5 files changed

+42
-10
lines changed

5 files changed

+42
-10
lines changed

lightning/src/blinded_path/message.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -92,20 +92,25 @@ impl Writeable for ReceiveTlvs {
9292
/// whenever the [`BlindedPath`] is used.
9393
/// The recipient can authenticate the message and utilize it for further processing
9494
/// if needed.
95-
#[derive(Clone, Debug)]
95+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
9696
pub enum MessageContext {
9797
/// Represents the data specific to [`OffersMessage`]
9898
///
9999
/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
100100
Offers(OffersContext),
101+
/// Represents a context for a blinded path used in a reply path when requesting a DNSSEC proof
102+
/// in a [`DNSResolverMessage`].
103+
///
104+
/// [`DNSResolverMessage`]: crate::onion_message::dns_resolution::DNSResolverMessage
105+
DNSResolver(DNSResolverContext),
101106
/// Represents custom data received in a Custom Onion Message.
102107
Custom(Vec<u8>),
103108
}
104109

105110
/// Contains the data specific to [`OffersMessage`]
106111
///
107112
/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
108-
#[derive(Clone, Debug)]
113+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
109114
pub enum OffersContext {
110115
/// Represents an unknown BOLT12 payment context.
111116
/// This variant is used when a message is sent without
@@ -122,6 +127,7 @@ pub enum OffersContext {
122127
impl_writeable_tlv_based_enum!(MessageContext, ;
123128
(0, Offers),
124129
(1, Custom),
130+
(3, DNSResolver),
125131
);
126132

127133
impl_writeable_tlv_based_enum!(OffersContext,
@@ -131,6 +137,24 @@ impl_writeable_tlv_based_enum!(OffersContext,
131137
},
132138
;);
133139

140+
/// Contains a simple nonce for use in a blinded path's context.
141+
///
142+
/// Such a context is required when receiving a [`DNSSECProof`] message.
143+
///
144+
/// [`DNSSECProof`]: crate::onion_message::dns_resolution::DNSSECProof
145+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
146+
pub struct DNSResolverContext {
147+
/// A nonce which uniquely describes a DNS resolution.
148+
///
149+
/// When we receive a DNSSEC proof message, we should check that it was sent over the blinded
150+
/// path we included in the request by comparing a stored nonce with this one.
151+
pub nonce: [u8; 16],
152+
}
153+
154+
impl_writeable_tlv_based!(DNSResolverContext, {
155+
(0, nonce, required),
156+
});
157+
134158
/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
135159
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
136160
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[ForwardNode], recipient_node_id: PublicKey,

lightning/src/ln/peer_handler.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use bitcoin::blockdata::constants::ChainHash;
1919
use bitcoin::secp256k1::{self, Secp256k1, SecretKey, PublicKey};
2020

21-
use crate::blinded_path::message::OffersContext;
21+
use crate::blinded_path::message::{DNSResolverContext, OffersContext};
2222
use crate::sign::{NodeSigner, Recipient};
2323
use crate::events::{MessageSendEvent, MessageSendEventsProvider};
2424
use crate::ln::types::ChannelId;
@@ -163,7 +163,7 @@ impl DNSResolverMessageHandler for IgnoringMessageHandler {
163163
) -> ResponseInstruction<DNSResolverMessage> {
164164
ResponseInstruction::NoResponse
165165
}
166-
fn dnssec_proof(&self, _message: DNSSECProof) {}
166+
fn dnssec_proof(&self, _message: DNSSECProof, _context: DNSResolverContext) {}
167167
}
168168
impl CustomOnionMessageHandler for IgnoringMessageHandler {
169169
type CustomMessage = Infallible;

lightning/src/onion_message/dns_resolution.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
1818
use dnssec_prover::rr::Name;
1919

20+
use crate::blinded_path::message::DNSResolverContext;
2021
use crate::io;
2122
use crate::ln::msgs::DecodeError;
2223
use crate::onion_message::messenger::{PendingOnionMessage, Responder, ResponseInstruction};
@@ -39,7 +40,7 @@ pub trait DNSResolverMessageHandler {
3940
/// Handle a [`DNSSECProof`] message (in response to a [`DNSSECQuery`] we presumably sent).
4041
///
4142
/// With this, we should be able to validate the DNS record we requested.
42-
fn dnssec_proof(&self, message: DNSSECProof);
43+
fn dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext);
4344

4445
/// Release any [`DNSResolverMessage`]s that need to be sent.
4546
#[cfg(not(c_bindings))]

lightning/src/onion_message/functional_tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//! Onion message testing and test utilities live here.
1111
1212
use crate::blinded_path::{BlindedPath, EmptyNodeIdLookUp};
13-
use crate::blinded_path::message::{ForwardNode, MessageContext, OffersContext};
13+
use crate::blinded_path::message::{DNSResolverContext, ForwardNode, MessageContext, OffersContext};
1414
use crate::events::{Event, EventsProvider};
1515
use crate::ln::features::{ChannelFeatures, InitFeatures};
1616
use crate::ln::msgs::{self, DecodeError, OnionMessageHandler};
@@ -102,7 +102,7 @@ impl DNSResolverMessageHandler for TestDNSResolverMessageHandler {
102102
) -> ResponseInstruction<DNSResolverMessage> {
103103
ResponseInstruction::NoResponse
104104
}
105-
fn dnssec_proof(&self, _message: DNSSECProof) {}
105+
fn dnssec_proof(&self, _message: DNSSECProof, _context: DNSResolverContext) {}
106106
}
107107

108108
#[derive(Clone, Debug, PartialEq)]

lightning/src/onion_message/messenger.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,9 @@ where
975975
(ParsedOnionMessageContents::Custom(_), Some(MessageContext::Custom(_))) => {
976976
Ok(PeeledOnion::Receive(message, context, reply_path))
977977
}
978+
(ParsedOnionMessageContents::DNSResolver(_), Some(MessageContext::DNSResolver(_))) => {
979+
Ok(PeeledOnion::Receive(message, context, reply_path))
980+
}
978981
_ => {
979982
log_trace!(logger, "Received message was sent on a blinded path with the wrong context.");
980983
Err(())
@@ -1480,7 +1483,7 @@ where
14801483
let context = match context {
14811484
None => OffersContext::Unknown {},
14821485
Some(MessageContext::Offers(context)) => context,
1483-
Some(MessageContext::Custom(_)) => {
1486+
Some(_) => {
14841487
debug_assert!(false, "Shouldn't have triggered this case.");
14851488
return
14861489
}
@@ -1504,13 +1507,17 @@ where
15041507
let _ = self.handle_onion_message_response(response);
15051508
},
15061509
ParsedOnionMessageContents::DNSResolver(DNSResolverMessage::DNSSECProof(msg)) => {
1507-
self.dns_resolver_handler.dnssec_proof(msg);
1510+
let context = match context {
1511+
Some(MessageContext::DNSResolver(context)) => context,
1512+
_ => return,
1513+
};
1514+
self.dns_resolver_handler.dnssec_proof(msg, context);
15081515
},
15091516
ParsedOnionMessageContents::Custom(msg) => {
15101517
let context = match context {
15111518
None => None,
15121519
Some(MessageContext::Custom(data)) => Some(data),
1513-
Some(MessageContext::Offers(_)) => {
1520+
Some(_) => {
15141521
debug_assert!(false, "Shouldn't have triggered this case.");
15151522
return
15161523
}

0 commit comments

Comments
 (0)