From 1cf03930561cd49017cbff8237ef45cdab379fb0 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 30 Sep 2024 16:16:36 +0000 Subject: [PATCH] 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. --- lightning/Cargo.toml | 2 + lightning/src/blinded_path/message.rs | 8 +- lightning/src/onion_message/dns_resolution.rs | 146 ++++++++++++++++++ lightning/src/onion_message/mod.rs | 1 + lightning/src/util/ser.rs | 10 ++ 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 lightning/src/onion_message/dns_resolution.rs diff --git a/lightning/Cargo.toml b/lightning/Cargo.toml index 3a1939733..e0eb50431 100644 --- a/lightning/Cargo.toml +++ b/lightning/Cargo.toml @@ -43,8 +43,10 @@ lightning-invoice = { version = "0.32.0", path = "../lightning-invoice", default bech32 = { version = "0.9.1", default-features = false } bitcoin = { version = "0.32.2", default-features = false, features = ["secp-recovery"] } +dnssec-prover = { version = "0.6", default-features = false } hashbrown = { version = "0.13", default-features = false } possiblyrandom = { version = "0.2", path = "../possiblyrandom", default-features = false } + regex = { version = "1.5.6", optional = true } backtrace = { version = "0.3", optional = true } diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index 5b6b5fa6d..2cad33887 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -285,7 +285,9 @@ pub enum MessageContext { /// [`AsyncPaymentsMessage`]: crate::onion_message::async_payments::AsyncPaymentsMessage AsyncPayments(AsyncPaymentsContext), /// Represents a context for a blinded path used in a reply path when requesting a DNSSEC proof - /// in a `DNSResolverMessage`. + /// in a [`DNSResolverMessage`]. + /// + /// [`DNSResolverMessage`]: crate::onion_message::dns_resolution::DNSResolverMessage DNSResolver(DNSResolverContext), /// Context specific to a [`CustomOnionMessageHandler::CustomMessage`]. /// @@ -434,7 +436,9 @@ impl_writeable_tlv_based_enum!(AsyncPaymentsContext, /// Contains a simple nonce for use in a blinded path's context. /// -/// Such a context is required when receiving a `DNSSECProof` message. +/// Such a context is required when receiving a [`DNSSECProof`] message. +/// +/// [`DNSSECProof`]: crate::onion_message::dns_resolution::DNSSECProof #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct DNSResolverContext { /// A nonce which uniquely describes a DNS resolution. diff --git a/lightning/src/onion_message/dns_resolution.rs b/lightning/src/onion_message/dns_resolution.rs new file mode 100644 index 000000000..3d531264a --- /dev/null +++ b/lightning/src/onion_message/dns_resolution.rs @@ -0,0 +1,146 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +//! This module defines message handling for DNSSEC proof fetching using [bLIP 32]. +//! +//! It contains [`DNSResolverMessage`]s as well as a [`DNSResolverMessageHandler`] trait to handle +//! such messages using an [`OnionMessenger`]. +//! +//! [bLIP 32]: https://github.com/lightning/blips/blob/master/blip-0032.md +//! [`OnionMessenger`]: super::messenger::OnionMessenger + +use dnssec_prover::rr::Name; + +use crate::blinded_path::message::DNSResolverContext; +use crate::io; +use crate::ln::msgs::DecodeError; +use crate::onion_message::messenger::{MessageSendInstructions, Responder, ResponseInstruction}; +use crate::onion_message::packet::OnionMessageContents; +use crate::prelude::*; +use crate::util::ser::{Hostname, Readable, ReadableArgs, Writeable, Writer}; + +/// A handler for an [`OnionMessage`] containing a DNS(SEC) query or a DNSSEC proof +/// +/// [`OnionMessage`]: crate::ln::msgs::OnionMessage +pub trait DNSResolverMessageHandler { + /// Handle a [`DNSSECQuery`] message. + /// + /// If we provide DNS resolution services to third parties, we should respond with a + /// [`DNSSECProof`] message. + fn handle_dnssec_query( + &self, message: DNSSECQuery, responder: Option, + ) -> Option<(DNSResolverMessage, ResponseInstruction)>; + + /// Handle a [`DNSSECProof`] message (in response to a [`DNSSECQuery`] we presumably sent). + /// + /// With this, we should be able to validate the DNS record we requested. + fn handle_dnssec_proof(&self, message: DNSSECProof, context: DNSResolverContext); + + /// Release any [`DNSResolverMessage`]s that need to be sent. + fn release_pending_messages(&self) -> Vec<(DNSResolverMessage, MessageSendInstructions)> { + vec![] + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +/// An enum containing the possible onion messages which are used uses to request and receive +/// DNSSEC proofs. +pub enum DNSResolverMessage { + /// A query requesting a DNSSEC proof + DNSSECQuery(DNSSECQuery), + /// A response containing a DNSSEC proof + DNSSECProof(DNSSECProof), +} + +const DNSSEC_QUERY_TYPE: u64 = 65536; +const DNSSEC_PROOF_TYPE: u64 = 65538; + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +/// A message which is sent to a DNSSEC prover requesting a DNSSEC proof for the given name. +pub struct DNSSECQuery(pub Name); + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +/// A message which is sent in response to [`DNSSECQuery`] containing a DNSSEC proof. +pub struct DNSSECProof { + /// The name which the query was for. The proof may not contain a DNS RR for exactly this name + /// if it contains a wildcard RR which contains this name instead. + pub name: Name, + /// An [RFC 9102 DNSSEC AuthenticationChain] providing a DNSSEC proof. + /// + /// [RFC 9102 DNSSEC AuthenticationChain]: https://www.rfc-editor.org/rfc/rfc9102.html#name-dnssec-authentication-chain + pub proof: Vec, +} + +impl DNSResolverMessage { + /// Returns whether `tlv_type` corresponds to a TLV record for DNS Resolvers. + pub fn is_known_type(tlv_type: u64) -> bool { + match tlv_type { + DNSSEC_QUERY_TYPE | DNSSEC_PROOF_TYPE => true, + _ => false, + } + } +} + +impl Writeable for DNSResolverMessage { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + match self { + Self::DNSSECQuery(DNSSECQuery(q)) => { + (q.as_str().len() as u8).write(w)?; + w.write_all(&q.as_str().as_bytes()) + }, + Self::DNSSECProof(DNSSECProof { name, proof }) => { + (name.as_str().len() as u8).write(w)?; + w.write_all(&name.as_str().as_bytes())?; + proof.write(w) + }, + } + } +} + +impl ReadableArgs for DNSResolverMessage { + fn read(r: &mut R, message_type: u64) -> Result { + match message_type { + DNSSEC_QUERY_TYPE => { + let s = Hostname::read(r)?; + let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?; + Ok(DNSResolverMessage::DNSSECQuery(DNSSECQuery(name))) + }, + DNSSEC_PROOF_TYPE => { + let s = Hostname::read(r)?; + let name = s.try_into().map_err(|_| DecodeError::InvalidValue)?; + let proof = Readable::read(r)?; + Ok(DNSResolverMessage::DNSSECProof(DNSSECProof { name, proof })) + }, + _ => Err(DecodeError::InvalidValue), + } + } +} + +impl OnionMessageContents for DNSResolverMessage { + #[cfg(c_bindings)] + fn msg_type(&self) -> String { + match self { + DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query".to_string(), + DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof".to_string(), + } + } + #[cfg(not(c_bindings))] + fn msg_type(&self) -> &'static str { + match self { + DNSResolverMessage::DNSSECQuery(_) => "DNS(SEC) Query", + DNSResolverMessage::DNSSECProof(_) => "DNSSEC Proof", + } + } + fn tlv_type(&self) -> u64 { + match self { + DNSResolverMessage::DNSSECQuery(_) => DNSSEC_QUERY_TYPE, + DNSResolverMessage::DNSSECProof(_) => DNSSEC_PROOF_TYPE, + } + } +} diff --git a/lightning/src/onion_message/mod.rs b/lightning/src/onion_message/mod.rs index 8cdf098a3..a5735e372 100644 --- a/lightning/src/onion_message/mod.rs +++ b/lightning/src/onion_message/mod.rs @@ -22,6 +22,7 @@ //! [`OnionMessenger`]: self::messenger::OnionMessenger pub mod async_payments; +pub mod dns_resolution; pub mod messenger; pub mod offers; pub mod packet; diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 99d20b927..b847f9eeb 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -37,6 +37,9 @@ use bitcoin::hashes::hmac::Hmac; use bitcoin::hashes::sha256d::Hash as Sha256dHash; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hash_types::{Txid, BlockHash}; + +use dnssec_prover::rr::Name; + use core::time::Duration; use crate::chain::ClaimId; use crate::ln::msgs::DecodeError; @@ -1551,6 +1554,13 @@ impl Readable for Hostname { } } +impl TryInto for Hostname { + type Error = (); + fn try_into(self) -> Result { + Name::try_from(self.0) + } +} + /// This is not exported to bindings users as `Duration`s are simply mapped as ints. impl Writeable for Duration { #[inline] -- 2.39.5