Skip to content
11 changes: 8 additions & 3 deletions fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ use lightning::routing::router::{
InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters, Router,
};
use lightning::sign::{
EntropySource, InMemorySigner, NodeSigner, PeerStorageKey, Recipient, SignerProvider,
EntropySource, InMemorySigner, NodeSigner, PeerStorageKey, ReceiveAuthKey, Recipient,
SignerProvider,
};
use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::util::config::UserConfig;
Expand Down Expand Up @@ -142,8 +143,8 @@ impl MessageRouter for FuzzRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _local_node_receive_key: ReceiveAuthKey,
_context: MessageContext, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedMessagePath>, ()> {
unreachable!()
}
Expand Down Expand Up @@ -347,6 +348,10 @@ impl NodeSigner for KeyProvider {
PeerStorageKey { inner: [42; 32] }
}

fn get_receive_auth_key(&self) -> ReceiveAuthKey {
ReceiveAuthKey([41; 32])
}

fn sign_bolt12_invoice(
&self, _invoice: &UnsignedBolt12Invoice,
) -> Result<schnorr::Signature, ()> {
Expand Down
11 changes: 8 additions & 3 deletions fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ use lightning::routing::router::{
};
use lightning::routing::utxo::UtxoLookup;
use lightning::sign::{
EntropySource, InMemorySigner, NodeSigner, PeerStorageKey, Recipient, SignerProvider,
EntropySource, InMemorySigner, NodeSigner, PeerStorageKey, ReceiveAuthKey, Recipient,
SignerProvider,
};
use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::util::config::{ChannelConfig, UserConfig};
Expand Down Expand Up @@ -173,8 +174,8 @@ impl MessageRouter for FuzzRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _local_node_receive_key: ReceiveAuthKey,
_context: MessageContext, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedMessagePath>, ()> {
unreachable!()
}
Expand Down Expand Up @@ -438,6 +439,10 @@ impl NodeSigner for KeyProvider {
fn get_peer_storage_key(&self) -> PeerStorageKey {
PeerStorageKey { inner: [42; 32] }
}

fn get_receive_auth_key(&self) -> ReceiveAuthKey {
ReceiveAuthKey([41; 32])
}
}

impl SignerProvider for KeyProvider {
Expand Down
12 changes: 9 additions & 3 deletions fuzz/src/onion_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ use lightning::onion_message::messenger::{
};
use lightning::onion_message::offers::{OffersMessage, OffersMessageHandler};
use lightning::onion_message::packet::OnionMessageContents;
use lightning::sign::{EntropySource, NodeSigner, PeerStorageKey, Recipient, SignerProvider};
use lightning::sign::{
EntropySource, NodeSigner, PeerStorageKey, ReceiveAuthKey, Recipient, SignerProvider,
};
use lightning::types::features::InitFeatures;
use lightning::util::logger::Logger;
use lightning::util::ser::{LengthReadable, Writeable, Writer};
Expand Down Expand Up @@ -104,8 +106,8 @@ impl MessageRouter for TestMessageRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _local_node_receive_key: ReceiveAuthKey,
_context: MessageContext, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedMessagePath>, ()> {
unreachable!()
}
Expand Down Expand Up @@ -278,6 +280,10 @@ impl NodeSigner for KeyProvider {
fn get_peer_storage_key(&self) -> PeerStorageKey {
unreachable!()
}

fn get_receive_auth_key(&self) -> ReceiveAuthKey {
ReceiveAuthKey([41; 32])
}
}

impl SignerProvider for KeyProvider {
Expand Down
26 changes: 20 additions & 6 deletions lightning-dns-resolver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ mod test {
AOnionMessenger, Destination, MessageRouter, OnionMessagePath, OnionMessenger,
};
use lightning::routing::router::RouteParametersConfig;
use lightning::sign::{KeysManager, NodeSigner, Recipient};
use lightning::sign::{KeysManager, NodeSigner, ReceiveAuthKey, Recipient};
use lightning::types::features::InitFeatures;
use lightning::types::payment::PaymentHash;
use lightning::util::logger::Logger;
Expand Down Expand Up @@ -230,11 +230,18 @@ mod test {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, recipient: PublicKey, context: MessageContext, _peers: Vec<PublicKey>,
secp_ctx: &Secp256k1<T>,
&self, recipient: PublicKey, local_node_receive_key: ReceiveAuthKey,
context: MessageContext, _peers: Vec<PublicKey>, secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedMessagePath>, ()> {
let keys = KeysManager::new(&[0; 32], 42, 43);
Ok(vec![BlindedMessagePath::one_hop(recipient, context, &keys, secp_ctx).unwrap()])
Ok(vec![BlindedMessagePath::one_hop(
recipient,
local_node_receive_key,
context,
&keys,
secp_ctx,
)
.unwrap()])
}
}
impl Deref for DirectlyConnectedRouter {
Expand Down Expand Up @@ -336,8 +343,15 @@ mod test {
let (msg, context) =
payer.resolver.resolve_name(payment_id, name.clone(), &*payer_keys).unwrap();
let query_context = MessageContext::DNSResolver(context);
let reply_path =
BlindedMessagePath::one_hop(payer_id, query_context, &*payer_keys, &secp_ctx).unwrap();
let receive_key = payer_keys.get_receive_auth_key();
let reply_path = BlindedMessagePath::one_hop(
payer_id,
receive_key,
query_context,
&*payer_keys,
&secp_ctx,
)
.unwrap();
payer.pending_messages.lock().unwrap().push((
DNSResolverMessage::DNSSECQuery(msg),
MessageSendInstructions::WithSpecifiedReplyPath {
Expand Down
91 changes: 28 additions & 63 deletions lightning/src/blinded_path/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,10 @@ use crate::offers::nonce::Nonce;
use crate::offers::offer::OfferId;
use crate::onion_message::packet::ControlTlvs;
use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
use crate::sign::{EntropySource, NodeSigner, Recipient};
use crate::sign::{EntropySource, NodeSigner, ReceiveAuthKey, Recipient};
use crate::types::payment::PaymentHash;
use crate::util::scid_utils;
use crate::util::ser::{FixedLengthReader, LengthReadableArgs, Readable, Writeable, Writer};
use bitcoin::hashes::hmac::Hmac;
use bitcoin::hashes::sha256::Hash as Sha256;

use core::mem;
use core::ops::Deref;
Expand All @@ -57,13 +55,13 @@ impl Readable for BlindedMessagePath {
impl BlindedMessagePath {
/// Create a one-hop blinded path for a message.
pub fn one_hop<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES,
secp_ctx: &Secp256k1<T>,
recipient_node_id: PublicKey, local_node_receive_key: ReceiveAuthKey,
context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>,
) -> Result<Self, ()>
where
ES::Target: EntropySource,
{
Self::new(&[], recipient_node_id, context, entropy_source, secp_ctx)
Self::new(&[], recipient_node_id, local_node_receive_key, context, entropy_source, secp_ctx)
}

/// Create a path for an onion message, to be forwarded along `node_pks`. The last node
Expand All @@ -73,7 +71,8 @@ impl BlindedMessagePath {
// TODO: make all payloads the same size with padding + add dummy hops
pub fn new<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey,
context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>,
local_node_receive_key: ReceiveAuthKey, context: MessageContext, entropy_source: ES,
secp_ctx: &Secp256k1<T>,
) -> Result<Self, ()>
where
ES::Target: EntropySource,
Expand All @@ -94,6 +93,7 @@ impl BlindedMessagePath {
recipient_node_id,
context,
&blinding_secret,
local_node_receive_key,
)
.map_err(|_| ())?,
}))
Expand Down Expand Up @@ -404,12 +404,6 @@ pub enum OffersContext {
/// [`Refund`]: crate::offers::refund::Refund
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
nonce: Nonce,

/// Authentication code for the [`PaymentId`], which should be checked when the context is
/// used with an [`InvoiceError`].
///
/// [`InvoiceError`]: crate::offers::invoice_error::InvoiceError
hmac: Option<Hmac<Sha256>>,
},
/// Context used by a [`BlindedMessagePath`] as a reply path for a [`Bolt12Invoice`].
///
Expand All @@ -422,19 +416,6 @@ pub enum OffersContext {
///
/// [`Bolt12Invoice::payment_hash`]: crate::offers::invoice::Bolt12Invoice::payment_hash
payment_hash: PaymentHash,

/// A nonce used for authenticating that a received [`InvoiceError`] is for a valid
/// sent [`Bolt12Invoice`].
///
/// [`InvoiceError`]: crate::offers::invoice_error::InvoiceError
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
nonce: Nonce,

/// Authentication code for the [`PaymentHash`], which should be checked when the context is
/// used to log the received [`InvoiceError`].
///
/// [`InvoiceError`]: crate::offers::invoice_error::InvoiceError
hmac: Hmac<Sha256>,
},
}

Expand Down Expand Up @@ -542,35 +523,12 @@ pub enum AsyncPaymentsContext {
///
/// [`Offer`]: crate::offers::offer::Offer
payment_id: PaymentId,
/// A nonce used for authenticating that a [`ReleaseHeldHtlc`] message is valid for a preceding
/// [`HeldHtlcAvailable`] message.
///
/// [`ReleaseHeldHtlc`]: crate::onion_message::async_payments::ReleaseHeldHtlc
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
nonce: Nonce,
/// Authentication code for the [`PaymentId`].
///
/// Prevents the recipient from being able to deanonymize us by creating a blinded path to us
/// containing the expected [`PaymentId`].
hmac: Hmac<Sha256>,
},
/// Context contained within the [`BlindedMessagePath`]s we put in static invoices, provided back
/// to us in corresponding [`HeldHtlcAvailable`] messages.
///
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
InboundPayment {
/// A nonce used for authenticating that a [`HeldHtlcAvailable`] message is valid for a
/// preceding static invoice.
///
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
nonce: Nonce,
/// Authentication code for the [`HeldHtlcAvailable`] message.
///
/// Prevents nodes from creating their own blinded path to us, sending a [`HeldHtlcAvailable`]
/// message and trivially getting notified whenever we come online.
///
/// [`HeldHtlcAvailable`]: crate::onion_message::async_payments::HeldHtlcAvailable
hmac: Hmac<Sha256>,
/// The time as duration since the Unix epoch at which this path expires and messages sent over
/// it should be ignored. Without this, anyone with the path corresponding to this context is
/// able to trivially ask if we're online forever.
Expand All @@ -585,19 +543,27 @@ impl_writeable_tlv_based_enum!(MessageContext,
{3, DNSResolver} => (),
);

// NOTE:
// Several TLV fields (`nonce`, `hmac`, etc.) were removed in LDK v0.2
// following the introduction of `ReceiveAuthKey`-based authentication for
// inbound `BlindedMessagePath`s. These fields are now commented out and
// their `type` values must not be reused unless support for LDK v0.2
// and earlier is fully dropped.
//
// For context-specific removals, see the commented-out fields within each enum variant.
impl_writeable_tlv_based_enum!(OffersContext,
(0, InvoiceRequest) => {
(0, nonce, required),
},
(1, OutboundPayment) => {
(0, payment_id, required),
(1, nonce, required),
(2, hmac, option),
// Removed: (2, hmac, option)
},
(2, InboundPayment) => {
(0, payment_hash, required),
(1, nonce, required),
(2, hmac, required)
// Removed: (1, nonce, required),
// Removed: (2, hmac, required)
},
(3, StaticInvoiceRequested) => {
(0, recipient_id, required),
Expand All @@ -609,12 +575,12 @@ impl_writeable_tlv_based_enum!(OffersContext,
impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
(0, OutboundPayment) => {
(0, payment_id, required),
(2, nonce, required),
(4, hmac, required),
// Removed: (2, nonce, required),
// Removed: (4, hmac, required),
},
(1, InboundPayment) => {
(0, nonce, required),
(2, hmac, required),
// Removed: (0, nonce, required),
// Removed: (2, hmac, required),
(4, path_absolute_expiry, required),
},
(2, OfferPaths) => {
Expand Down Expand Up @@ -642,10 +608,8 @@ impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
/// [`DNSSECProof`]: crate::onion_message::dns_resolution::DNSSECProof
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct DNSResolverContext {
/// A nonce which uniquely describes a DNS resolution.
///
/// When we receive a DNSSEC proof message, we should check that it was sent over the blinded
/// path we included in the request by comparing a stored nonce with this one.
/// A nonce which uniquely describes a DNS resolution, useful for looking up metadata about the
/// request.
pub nonce: [u8; 16],
}

Expand All @@ -661,18 +625,19 @@ pub(crate) const MESSAGE_PADDING_ROUND_OFF: usize = 100;
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[MessageForwardNode],
recipient_node_id: PublicKey, context: MessageContext, session_priv: &SecretKey,
local_node_receive_key: ReceiveAuthKey,
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
let pks = intermediate_nodes
.iter()
.map(|node| node.node_id)
.chain(core::iter::once(recipient_node_id));
.map(|node| (node.node_id, None))
.chain(core::iter::once((recipient_node_id, Some(local_node_receive_key))));
let is_compact = intermediate_nodes.iter().any(|node| node.short_channel_id.is_some());

let tlvs = pks
.clone()
.skip(1) // The first node's TLVs contains the next node's pubkey
.zip(intermediate_nodes.iter().map(|node| node.short_channel_id))
.map(|(pubkey, scid)| match scid {
.map(|((pubkey, _), scid)| match scid {
Some(scid) => NextMessageHop::ShortChannelId(scid),
None => NextMessageHop::NodeId(pubkey),
})
Expand Down
6 changes: 4 additions & 2 deletions lightning/src/blinded_path/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,8 +664,10 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[PaymentForwardNode], payee_node_id: PublicKey,
payee_tlvs: ReceiveTlvs, session_priv: &SecretKey,
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
let pks =
intermediate_nodes.iter().map(|node| node.node_id).chain(core::iter::once(payee_node_id));
let pks = intermediate_nodes
.iter()
.map(|node| (node.node_id, None))
.chain(core::iter::once((payee_node_id, None)));
let tlvs = intermediate_nodes
.iter()
.map(|node| BlindedPaymentTlvsRef::Forward(&node.tlvs))
Expand Down
Loading
Loading