Skip to content

Commit 1cf0393

Browse files
committed
Add DNS(SEC) query and proof messages and onion message handler
This creates the initial DNSSEC proof and query messages in a new module in `onion_message`, as well as a new message handler to handle them. In the coming commits, a default implementation will be added which verifies DNSSEC proofs which can be used to resolve BIP 353 URIs without relying on anything outside of the lightning network.
1 parent 151a8a1 commit 1cf0393

File tree

5 files changed

+165
-2
lines changed

5 files changed

+165
-2
lines changed

lightning/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ lightning-invoice = { version = "0.32.0", path = "../lightning-invoice", default
4343
bech32 = { version = "0.9.1", default-features = false }
4444
bitcoin = { version = "0.32.2", default-features = false, features = ["secp-recovery"] }
4545

46+
dnssec-prover = { version = "0.6", default-features = false }
4647
hashbrown = { version = "0.13", default-features = false }
4748
possiblyrandom = { version = "0.2", path = "../possiblyrandom", default-features = false }
49+
4850
regex = { version = "1.5.6", optional = true }
4951
backtrace = { version = "0.3", optional = true }
5052

lightning/src/blinded_path/message.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,9 @@ pub enum MessageContext {
285285
/// [`AsyncPaymentsMessage`]: crate::onion_message::async_payments::AsyncPaymentsMessage
286286
AsyncPayments(AsyncPaymentsContext),
287287
/// Represents a context for a blinded path used in a reply path when requesting a DNSSEC proof
288-
/// in a `DNSResolverMessage`.
288+
/// in a [`DNSResolverMessage`].
289+
///
290+
/// [`DNSResolverMessage`]: crate::onion_message::dns_resolution::DNSResolverMessage
289291
DNSResolver(DNSResolverContext),
290292
/// Context specific to a [`CustomOnionMessageHandler::CustomMessage`].
291293
///
@@ -434,7 +436,9 @@ impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
434436

435437
/// Contains a simple nonce for use in a blinded path's context.
436438
///
437-
/// Such a context is required when receiving a `DNSSECProof` message.
439+
/// Such a context is required when receiving a [`DNSSECProof`] message.
440+
///
441+
/// [`DNSSECProof`]: crate::onion_message::dns_resolution::DNSSECProof
438442
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
439443
pub struct DNSResolverContext {
440444
/// A nonce which uniquely describes a DNS resolution.
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! This module defines message handling for DNSSEC proof fetching using [bLIP 32].
11+
//!
12+
//! It contains [`DNSResolverMessage`]s as well as a [`DNSResolverMessageHandler`] trait to handle
13+
//! such messages using an [`OnionMessenger`].
14+
//!
15+
//! [bLIP 32]: https://github.com/lightning/blips/blob/master/blip-0032.md
16+
//! [`OnionMessenger`]: super::messenger::OnionMessenger
17+
18+
use dnssec_prover::rr::Name;
19+
20+
use crate::blinded_path::message::DNSResolverContext;
21+
use crate::io;
22+
use crate::ln::msgs::DecodeError;
23+
use crate::onion_message::messenger::{MessageSendInstructions, Responder, ResponseInstruction};
24+
use crate::onion_message::packet::OnionMessageContents;
25+
use crate::prelude::*;
26+
use crate::util::ser::{Hostname, Readable, ReadableArgs, Writeable, Writer};
27+
28+
/// A handler for an [`OnionMessage`] containing a DNS(SEC) query or a DNSSEC proof
29+
///
30+
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
31+
pub trait DNSResolverMessageHandler {
32+
/// Handle a [`DNSSECQuery`] message.
33+
///
34+
/// If we provide DNS resolution services to third parties, we should respond with a
35+
/// [`DNSSECProof`] message.
36+
fn handle_dnssec_query(
37+
&self, message: DNSSECQuery, responder: Option<Responder>,
38+
) -> Option<(DNSResolverMessage, ResponseInstruction)>;
39+
40+
/// Handle a [`DNSSECProof`] message (in response to a [`DNSSECQuery`] we presumably sent).
41+
///
42+
/// With this, we should be able to validate the DNS record we requested.
43+
fn handle_dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext);
44+
45+
/// Release any [`DNSResolverMessage`]s that need to be sent.
46+
fn release_pending_messages(&self) -> Vec<(DNSResolverMessage, MessageSendInstructions)> {
47+
vec![]
48+
}
49+
}
50+
51+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
52+
/// An enum containing the possible onion messages which are used uses to request and receive
53+
/// DNSSEC proofs.
54+
pub enum DNSResolverMessage {
55+
/// A query requesting a DNSSEC proof
56+
DNSSECQuery(DNSSECQuery),
57+
/// A response containing a DNSSEC proof
58+
DNSSECProof(DNSSECProof),
59+
}
60+
61+
const DNSSEC_QUERY_TYPE: u64 = 65536;
62+
const DNSSEC_PROOF_TYPE: u64 = 65538;
63+
64+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
65+
/// A message which is sent to a DNSSEC prover requesting a DNSSEC proof for the given name.
66+
pub struct DNSSECQuery(pub Name);
67+
68+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
69+
/// A message which is sent in response to [`DNSSECQuery`] containing a DNSSEC proof.
70+
pub struct DNSSECProof {
71+
/// The name which the query was for. The proof may not contain a DNS RR for exactly this name
72+
/// if it contains a wildcard RR which contains this name instead.
73+
pub name: Name,
74+
/// An [RFC 9102 DNSSEC AuthenticationChain] providing a DNSSEC proof.
75+
///
76+
/// [RFC 9102 DNSSEC AuthenticationChain]: https://www.rfc-editor.org/rfc/rfc9102.html#name-dnssec-authentication-chain
77+
pub proof: Vec<u8>,
78+
}
79+
80+
impl DNSResolverMessage {
81+
/// Returns whether `tlv_type` corresponds to a TLV record for DNS Resolvers.
82+
pub fn is_known_type(tlv_type: u64) -> bool {
83+
match tlv_type {
84+
DNSSEC_QUERY_TYPE | DNSSEC_PROOF_TYPE => true,
85+
_ => false,
86+
}
87+
}
88+
}
89+
90+
impl Writeable for DNSResolverMessage {
91+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
92+
match self {
93+
Self::DNSSECQuery(DNSSECQuery(q)) => {
94+
(q.as_str().len() as u8).write(w)?;
95+
w.write_all(&q.as_str().as_bytes())
96+
},
97+
Self::DNSSECProof(DNSSECProof { name, proof }) => {
98+
(name.as_str().len() as u8).write(w)?;
99+
w.write_all(&name.as_str().as_bytes())?;
100+
proof.write(w)
101+
},
102+
}
103+
}
104+
}
105+
106+
impl ReadableArgs<u64> for DNSResolverMessage {
107+
fn read<R: io::Read>(r: &mut R, message_type: u64) -> Result<Self, DecodeError> {
108+
match message_type {
109+
DNSSEC_QUERY_TYPE => {
110+
let s = Hostname::read(r)?;
111+
let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
112+
Ok(DNSResolverMessage::DNSSECQuery(DNSSECQuery(name)))
113+
},
114+
DNSSEC_PROOF_TYPE => {
115+
let s = Hostname::read(r)?;
116+
let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?;
117+
let proof = Readable::read(r)?;
118+
Ok(DNSResolverMessage::DNSSECProof(DNSSECProof { name, proof }))
119+
},
120+
_ => Err(DecodeError::InvalidValue),
121+
}
122+
}
123+
}
124+
125+
impl OnionMessageContents for DNSResolverMessage {
126+
#[cfg(c_bindings)]
127+
fn msg_type(&self) -> String {
128+
match self {
129+
DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query".to_string(),
130+
DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof".to_string(),
131+
}
132+
}
133+
#[cfg(not(c_bindings))]
134+
fn msg_type(&self) -> &'static str {
135+
match self {
136+
DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query",
137+
DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof",
138+
}
139+
}
140+
fn tlv_type(&self) -> u64 {
141+
match self {
142+
DNSResolverMessage::DNSSECQuery(_) => DNSSEC_QUERY_TYPE,
143+
DNSResolverMessage::DNSSECProof(_) => DNSSEC_PROOF_TYPE,
144+
}
145+
}
146+
}

lightning/src/onion_message/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
//! [`OnionMessenger`]: self::messenger::OnionMessenger
2323
2424
pub mod async_payments;
25+
pub mod dns_resolution;
2526
pub mod messenger;
2627
pub mod offers;
2728
pub mod packet;

lightning/src/util/ser.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ use bitcoin::hashes::hmac::Hmac;
3737
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
3838
use bitcoin::hashes::sha256::Hash as Sha256;
3939
use bitcoin::hash_types::{Txid, BlockHash};
40+
41+
use dnssec_prover::rr::Name;
42+
4043
use core::time::Duration;
4144
use crate::chain::ClaimId;
4245
use crate::ln::msgs::DecodeError;
@@ -1551,6 +1554,13 @@ impl Readable for Hostname {
15511554
}
15521555
}
15531556

1557+
impl TryInto<Name> for Hostname {
1558+
type Error = ();
1559+
fn try_into(self) -> Result<Name, ()> {
1560+
Name::try_from(self.0)
1561+
}
1562+
}
1563+
15541564
/// This is not exported to bindings users as `Duration`s are simply mapped as ints.
15551565
impl Writeable for Duration {
15561566
#[inline]

0 commit comments

Comments
 (0)