Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1369,7 +1369,7 @@ impl PaymentTracker {
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&self.payment_ctr.to_be_bytes());
let hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array());
let secret = dest
let (secret, _no_metadata) = dest
.create_inbound_payment_for_hash(hash, None, 3600, None, None)
.expect("create_inbound_payment_for_hash failed");
assert!(self.payment_preimages.insert(hash, payment_preimage).is_none());
Expand Down
2 changes: 1 addition & 1 deletion lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -120,7 +120,7 @@ fn create_jit_invoice(
) -> Result<Bolt11Invoice, ()> {
// LSPS2 requires min_final_cltv_expiry_delta to be at least 2 more than usual.
let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2;
let (payment_hash, payment_secret) = node
let (payment_hash, payment_secret, _) = node
.node
.create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta), None)
.map_err(|e| {
Expand Down
26 changes: 21 additions & 5 deletions lightning/src/crypto/utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::{ecdsa::Signature, Message, Secp256k1, SecretKey, Signing};

use chacha20_poly1305::chacha20::{ChaCha20, Key, Nonce};

use crate::sign::EntropySource;

macro_rules! hkdf_extract_expand {
Expand All@@ -22,7 +24,7 @@ macro_rules! hkdf_extract_expand {
let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
(k1, k2)
}};
($salt: expr, $ikm: expr, 7) => {{
($salt: expr, $ikm: expr, 8) => {{
let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);

let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
Expand DownExpand Up@@ -50,18 +52,23 @@ macro_rules! hkdf_extract_expand {
hmac.input(&[7; 1]);
let k7 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7)
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
hmac.input(&k7);
hmac.input(&[8; 1]);
let k8 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7, k8)
}};
}

pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 2)
}

pub fn hkdf_extract_expand_7x(
pub fn hkdf_extract_expand_8x(
salt: &[u8], ikm: &[u8],
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 7)
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 8)
}

#[inline]
Expand DownExpand Up@@ -91,3 +98,12 @@ pub fn sign_with_aux_rand<C: Signing, ES: EntropySource>(
let sig = sign(ctx, msg, sk);
sig
}

pub fn apply_chacha20(key: [u8; 32], nonce: [u8; 16], data: &mut [u8]) {
ChaCha20::new_from_block(
Key::new(key),
Nonce::new(nonce[4..].try_into().unwrap()),
u32::from_le_bytes(nonce[..4].try_into().unwrap()),
)
.apply_keystream(data);
}
16 changes: 10 additions & 6 deletions lightning/src/ln/bolt11_payment_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,10 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -41,7 +43,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(50_000)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand DownExpand Up@@ -97,8 +99,10 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -107,7 +111,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
.payment_secret(payment_secret)
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand Down
58 changes: 27 additions & 31 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8462,7 +8462,7 @@ impl<
payment_data,
payment_context,
phantom_shared_secret,
onion_fields,
mut onion_fields,
has_recipient_created_payment_secret,
invoice_request_opt,
trampoline_shared_secret,
Expand DownExpand Up@@ -8603,7 +8603,7 @@ impl<
let verify_res = inbound_payment::verify(
payment_hash,
&payment_data,
onion_fields.payment_metadata.as_deref(),
onion_fields.payment_metadata.as_mut(),
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
&self.inbound_payment_key,
&self.logger,
Expand DownExpand Up@@ -14372,24 +14372,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
}
}

let (payment_hash, payment_secret) = match payment_hash {
let (payment_hash, payment_secret, payment_metadata) = match payment_hash {
Some(payment_hash) => {
let payment_secret = self
let (payment_secret, payment_metadata) = self
.create_inbound_payment_for_hash(
payment_hash, amount_msats,
invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
(payment_hash, payment_secret)
(payment_hash, payment_secret, payment_metadata)
},
None => {
self
.create_inbound_payment(
amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
},
Expand DownExpand Up@@ -14516,8 +14516,7 @@ pub struct Bolt11InvoiceParameters {
/// onion by the sender, available as [`RecipientOnionFields::payment_metadata`] via
/// [`Event::PaymentClaimable::onion_fields`].
///
/// Note that because it is exposed to the sender in the invoice you should consider encrypting
/// it. It is committed to, however, so cannot be modified by the sender.
/// The metadata itself is encrypted and HMAC'd before being stored in the BOLT 11 invoice.
pub payment_metadata: Option<Vec<u8>>,
}

Expand DownExpand Up@@ -15023,6 +15022,7 @@ impl<
|amount_msats, relative_expiry| {
self.create_inbound_payment(Some(amount_msats), relative_expiry, None, None)
.map_err(|()| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
},
None,
)?;
Expand All@@ -15033,8 +15033,8 @@ impl<
Ok(invoice)
}

/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
/// to pay us.
/// Gets a payment secret, payment hash, and encrypts the `payment_metadata` for use in an
/// invoice given to a third party wishing to pay us.
///
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
Expand DownExpand Up@@ -15065,8 +15065,8 @@ impl<
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(
&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<&[u8]>,
) -> Result<(PaymentHash, PaymentSecret), ()> {
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentHash, PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create(
&self.inbound_payment_key,
min_value_msat,
Expand All@@ -15078,8 +15078,8 @@ impl<
)
}

/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`] (for which the payment preimage is
/// stored external to LDK) and encrypts the `payment_metadata`.
///
/// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
Expand DownExpand Up@@ -15115,41 +15115,34 @@ impl<
/// Note that a malicious eavesdropper can intuit whether an inbound payment was created by
/// `create_inbound_payment` or `create_inbound_payment_for_hash` based on runtime.
///
/// # Note
///
/// If you register an inbound payment with this method, then serialize the `ChannelManager`, then
/// deserialize it with a node running 0.0.103 and earlier, the payment will fail to be received.
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(
&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>,
payment_metadata: Option<&[u8]>,
) -> Result<PaymentSecret, ()> {
payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create_from_hash(
&self.inbound_payment_key,
min_value_msat,
payment_hash,
invoice_expiry_delta_secs,
&self.entropy_source,
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
min_final_cltv_expiry,
payment_metadata,
)
}

/// Gets an LDK-generated payment preimage from a payment hash, metadata and secret that were
/// previously returned from [`create_inbound_payment`].
/// Gets an LDK-generated payment preimage from a payment hashand secret and decrypts the
/// metadata (if any) that were previously returned from [`create_inbound_payment`].
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
pub fn get_payment_preimage(
pub fn get_payment_preimage_decrypt_metadata(
&self, payment_hash: PaymentHash, payment_secret: PaymentSecret,
payment_metadata: Option<&[u8]>,
payment_metadata: Option<&mut [u8]>,
) -> Result<PaymentPreimage, APIError> {
let expanded_key = &self.inbound_payment_key;
inbound_payment::get_payment_preimage(
Expand DownExpand Up@@ -17235,7 +17228,9 @@ impl<
relative_expiry,
None,
None,
).map_err(|_| Bolt12SemanticError::InvalidAmount)
)
.map_err(|_| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
};

let (result, context) = match invoice_request {
Expand DownExpand Up@@ -22137,7 +22132,8 @@ pub mod bench {
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();
let (payment_secret, _no_payment_metadata) =
$node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();

$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000),
PaymentId(payment_hash.0),
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2800,7 +2800,7 @@ pub fn get_payment_preimage_hash(
let payment_preimage = PaymentPreimage([*payment_count; 32]);
*payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = recipient
let (payment_secret, _) = recipient
.node
.create_inbound_payment_for_hash(
payment_hash,
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Encrypt `payment_metadata` when we build the payment secret by TheBlueMatt · Pull Request #4628 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1369,7 +1369,7 @@ impl PaymentTracker {
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&self.payment_ctr.to_be_bytes());
let hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array());
let secret = dest
let (secret, _no_metadata) = dest
.create_inbound_payment_for_hash(hash, None, 3600, None, None)
.expect("create_inbound_payment_for_hash failed");
assert!(self.payment_preimages.insert(hash, payment_preimage).is_none());
Expand Down
2 changes: 1 addition & 1 deletion lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -120,7 +120,7 @@ fn create_jit_invoice(
) -> Result<Bolt11Invoice, ()> {
// LSPS2 requires min_final_cltv_expiry_delta to be at least 2 more than usual.
let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2;
let (payment_hash, payment_secret) = node
let (payment_hash, payment_secret, _) = node
.node
.create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta), None)
.map_err(|e| {
Expand Down
26 changes: 21 additions & 5 deletions lightning/src/crypto/utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::{ecdsa::Signature, Message, Secp256k1, SecretKey, Signing};

use chacha20_poly1305::chacha20::{ChaCha20, Key, Nonce};

use crate::sign::EntropySource;

macro_rules! hkdf_extract_expand {
Expand All@@ -22,7 +24,7 @@ macro_rules! hkdf_extract_expand {
let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
(k1, k2)
}};
($salt: expr, $ikm: expr, 7) => {{
($salt: expr, $ikm: expr, 8) => {{
let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);

let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
Expand DownExpand Up@@ -50,18 +52,23 @@ macro_rules! hkdf_extract_expand {
hmac.input(&[7; 1]);
let k7 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7)
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
hmac.input(&k7);
hmac.input(&[8; 1]);
let k8 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7, k8)
}};
}

pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 2)
}

pub fn hkdf_extract_expand_7x(
pub fn hkdf_extract_expand_8x(
salt: &[u8], ikm: &[u8],
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 7)
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 8)
}

#[inline]
Expand DownExpand Up@@ -91,3 +98,12 @@ pub fn sign_with_aux_rand<C: Signing, ES: EntropySource>(
let sig = sign(ctx, msg, sk);
sig
}

pub fn apply_chacha20(key: [u8; 32], nonce: [u8; 16], data: &mut [u8]) {
ChaCha20::new_from_block(
Key::new(key),
Nonce::new(nonce[4..].try_into().unwrap()),
u32::from_le_bytes(nonce[..4].try_into().unwrap()),
)
.apply_keystream(data);
}
16 changes: 10 additions & 6 deletions lightning/src/ln/bolt11_payment_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,10 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -41,7 +43,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(50_000)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand DownExpand Up@@ -97,8 +99,10 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -107,7 +111,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
.payment_secret(payment_secret)
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand Down
58 changes: 27 additions & 31 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8462,7 +8462,7 @@ impl<
payment_data,
payment_context,
phantom_shared_secret,
onion_fields,
mut onion_fields,
has_recipient_created_payment_secret,
invoice_request_opt,
trampoline_shared_secret,
Expand DownExpand Up@@ -8603,7 +8603,7 @@ impl<
let verify_res = inbound_payment::verify(
payment_hash,
&payment_data,
onion_fields.payment_metadata.as_deref(),
onion_fields.payment_metadata.as_mut(),
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
&self.inbound_payment_key,
&self.logger,
Expand DownExpand Up@@ -14372,24 +14372,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
}
}

let (payment_hash, payment_secret) = match payment_hash {
let (payment_hash, payment_secret, payment_metadata) = match payment_hash {
Some(payment_hash) => {
let payment_secret = self
let (payment_secret, payment_metadata) = self
.create_inbound_payment_for_hash(
payment_hash, amount_msats,
invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
(payment_hash, payment_secret)
(payment_hash, payment_secret, payment_metadata)
},
None => {
self
.create_inbound_payment(
amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
},
Expand DownExpand Up@@ -14516,8 +14516,7 @@ pub struct Bolt11InvoiceParameters {
/// onion by the sender, available as [`RecipientOnionFields::payment_metadata`] via
/// [`Event::PaymentClaimable::onion_fields`].
///
/// Note that because it is exposed to the sender in the invoice you should consider encrypting
/// it. It is committed to, however, so cannot be modified by the sender.
/// The metadata itself is encrypted and HMAC'd before being stored in the BOLT 11 invoice.
pub payment_metadata: Option<Vec<u8>>,
}

Expand DownExpand Up@@ -15023,6 +15022,7 @@ impl<
|amount_msats, relative_expiry| {
self.create_inbound_payment(Some(amount_msats), relative_expiry, None, None)
.map_err(|()| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
},
None,
)?;
Expand All@@ -15033,8 +15033,8 @@ impl<
Ok(invoice)
}

/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
/// to pay us.
/// Gets a payment secret, payment hash, and encrypts the `payment_metadata` for use in an
/// invoice given to a third party wishing to pay us.
///
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
Expand DownExpand Up@@ -15065,8 +15065,8 @@ impl<
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(
&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<&[u8]>,
) -> Result<(PaymentHash, PaymentSecret), ()> {
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentHash, PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create(
&self.inbound_payment_key,
min_value_msat,
Expand All@@ -15078,8 +15078,8 @@ impl<
)
}

/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`] (for which the payment preimage is
/// stored external to LDK) and encrypts the `payment_metadata`.
///
/// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
Expand DownExpand Up@@ -15115,41 +15115,34 @@ impl<
/// Note that a malicious eavesdropper can intuit whether an inbound payment was created by
/// `create_inbound_payment` or `create_inbound_payment_for_hash` based on runtime.
///
/// # Note
///
/// If you register an inbound payment with this method, then serialize the `ChannelManager`, then
/// deserialize it with a node running 0.0.103 and earlier, the payment will fail to be received.
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(
&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>,
payment_metadata: Option<&[u8]>,
) -> Result<PaymentSecret, ()> {
payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create_from_hash(
&self.inbound_payment_key,
min_value_msat,
payment_hash,
invoice_expiry_delta_secs,
&self.entropy_source,
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
min_final_cltv_expiry,
payment_metadata,
)
}

/// Gets an LDK-generated payment preimage from a payment hash, metadata and secret that were
/// previously returned from [`create_inbound_payment`].
/// Gets an LDK-generated payment preimage from a payment hashand secret and decrypts the
/// metadata (if any) that were previously returned from [`create_inbound_payment`].
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
pub fn get_payment_preimage(
pub fn get_payment_preimage_decrypt_metadata(
&self, payment_hash: PaymentHash, payment_secret: PaymentSecret,
payment_metadata: Option<&[u8]>,
payment_metadata: Option<&mut [u8]>,
) -> Result<PaymentPreimage, APIError> {
let expanded_key = &self.inbound_payment_key;
inbound_payment::get_payment_preimage(
Expand DownExpand Up@@ -17235,7 +17228,9 @@ impl<
relative_expiry,
None,
None,
).map_err(|_| Bolt12SemanticError::InvalidAmount)
)
.map_err(|_| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
};

let (result, context) = match invoice_request {
Expand DownExpand Up@@ -22137,7 +22132,8 @@ pub mod bench {
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();
let (payment_secret, _no_payment_metadata) =
$node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();

$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000),
PaymentId(payment_hash.0),
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2800,7 +2800,7 @@ pub fn get_payment_preimage_hash(
let payment_preimage = PaymentPreimage([*payment_count; 32]);
*payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = recipient
let (payment_secret, _) = recipient
.node
.create_inbound_payment_for_hash(
payment_hash,
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Encrypt `payment_metadata` when we build the payment secret by TheBlueMatt · Pull Request #4628 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1369,7 +1369,7 @@ impl PaymentTracker {
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&self.payment_ctr.to_be_bytes());
let hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array());
let secret = dest
let (secret, _no_metadata) = dest
.create_inbound_payment_for_hash(hash, None, 3600, None, None)
.expect("create_inbound_payment_for_hash failed");
assert!(self.payment_preimages.insert(hash, payment_preimage).is_none());
Expand Down
2 changes: 1 addition & 1 deletion lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -120,7 +120,7 @@ fn create_jit_invoice(
) -> Result<Bolt11Invoice, ()> {
// LSPS2 requires min_final_cltv_expiry_delta to be at least 2 more than usual.
let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2;
let (payment_hash, payment_secret) = node
let (payment_hash, payment_secret, _) = node
.node
.create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta), None)
.map_err(|e| {
Expand Down
26 changes: 21 additions & 5 deletions lightning/src/crypto/utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::{ecdsa::Signature, Message, Secp256k1, SecretKey, Signing};

use chacha20_poly1305::chacha20::{ChaCha20, Key, Nonce};

use crate::sign::EntropySource;

macro_rules! hkdf_extract_expand {
Expand All@@ -22,7 +24,7 @@ macro_rules! hkdf_extract_expand {
let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
(k1, k2)
}};
($salt: expr, $ikm: expr, 7) => {{
($salt: expr, $ikm: expr, 8) => {{
let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);

let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
Expand DownExpand Up@@ -50,18 +52,23 @@ macro_rules! hkdf_extract_expand {
hmac.input(&[7; 1]);
let k7 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7)
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
hmac.input(&k7);
hmac.input(&[8; 1]);
let k8 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7, k8)
}};
}

pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 2)
}

pub fn hkdf_extract_expand_7x(
pub fn hkdf_extract_expand_8x(
salt: &[u8], ikm: &[u8],
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 7)
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 8)
}

#[inline]
Expand DownExpand Up@@ -91,3 +98,12 @@ pub fn sign_with_aux_rand<C: Signing, ES: EntropySource>(
let sig = sign(ctx, msg, sk);
sig
}

pub fn apply_chacha20(key: [u8; 32], nonce: [u8; 16], data: &mut [u8]) {
ChaCha20::new_from_block(
Key::new(key),
Nonce::new(nonce[4..].try_into().unwrap()),
u32::from_le_bytes(nonce[..4].try_into().unwrap()),
)
.apply_keystream(data);
}
16 changes: 10 additions & 6 deletions lightning/src/ln/bolt11_payment_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,10 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -41,7 +43,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(50_000)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand DownExpand Up@@ -97,8 +99,10 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -107,7 +111,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
.payment_secret(payment_secret)
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand Down
58 changes: 27 additions & 31 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8462,7 +8462,7 @@ impl<
payment_data,
payment_context,
phantom_shared_secret,
onion_fields,
mut onion_fields,
has_recipient_created_payment_secret,
invoice_request_opt,
trampoline_shared_secret,
Expand DownExpand Up@@ -8603,7 +8603,7 @@ impl<
let verify_res = inbound_payment::verify(
payment_hash,
&payment_data,
onion_fields.payment_metadata.as_deref(),
onion_fields.payment_metadata.as_mut(),
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
&self.inbound_payment_key,
&self.logger,
Expand DownExpand Up@@ -14372,24 +14372,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
}
}

let (payment_hash, payment_secret) = match payment_hash {
let (payment_hash, payment_secret, payment_metadata) = match payment_hash {
Some(payment_hash) => {
let payment_secret = self
let (payment_secret, payment_metadata) = self
.create_inbound_payment_for_hash(
payment_hash, amount_msats,
invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
(payment_hash, payment_secret)
(payment_hash, payment_secret, payment_metadata)
},
None => {
self
.create_inbound_payment(
amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
},
Expand DownExpand Up@@ -14516,8 +14516,7 @@ pub struct Bolt11InvoiceParameters {
/// onion by the sender, available as [`RecipientOnionFields::payment_metadata`] via
/// [`Event::PaymentClaimable::onion_fields`].
///
/// Note that because it is exposed to the sender in the invoice you should consider encrypting
/// it. It is committed to, however, so cannot be modified by the sender.
/// The metadata itself is encrypted and HMAC'd before being stored in the BOLT 11 invoice.
pub payment_metadata: Option<Vec<u8>>,
}

Expand DownExpand Up@@ -15023,6 +15022,7 @@ impl<
|amount_msats, relative_expiry| {
self.create_inbound_payment(Some(amount_msats), relative_expiry, None, None)
.map_err(|()| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
},
None,
)?;
Expand All@@ -15033,8 +15033,8 @@ impl<
Ok(invoice)
}

/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
/// to pay us.
/// Gets a payment secret, payment hash, and encrypts the `payment_metadata` for use in an
/// invoice given to a third party wishing to pay us.
///
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
Expand DownExpand Up@@ -15065,8 +15065,8 @@ impl<
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(
&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<&[u8]>,
) -> Result<(PaymentHash, PaymentSecret), ()> {
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentHash, PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create(
&self.inbound_payment_key,
min_value_msat,
Expand All@@ -15078,8 +15078,8 @@ impl<
)
}

/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`] (for which the payment preimage is
/// stored external to LDK) and encrypts the `payment_metadata`.
///
/// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
Expand DownExpand Up@@ -15115,41 +15115,34 @@ impl<
/// Note that a malicious eavesdropper can intuit whether an inbound payment was created by
/// `create_inbound_payment` or `create_inbound_payment_for_hash` based on runtime.
///
/// # Note
///
/// If you register an inbound payment with this method, then serialize the `ChannelManager`, then
/// deserialize it with a node running 0.0.103 and earlier, the payment will fail to be received.
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(
&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>,
payment_metadata: Option<&[u8]>,
) -> Result<PaymentSecret, ()> {
payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create_from_hash(
&self.inbound_payment_key,
min_value_msat,
payment_hash,
invoice_expiry_delta_secs,
&self.entropy_source,
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
min_final_cltv_expiry,
payment_metadata,
)
}

/// Gets an LDK-generated payment preimage from a payment hash, metadata and secret that were
/// previously returned from [`create_inbound_payment`].
/// Gets an LDK-generated payment preimage from a payment hashand secret and decrypts the
/// metadata (if any) that were previously returned from [`create_inbound_payment`].
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
pub fn get_payment_preimage(
pub fn get_payment_preimage_decrypt_metadata(
&self, payment_hash: PaymentHash, payment_secret: PaymentSecret,
payment_metadata: Option<&[u8]>,
payment_metadata: Option<&mut [u8]>,
) -> Result<PaymentPreimage, APIError> {
let expanded_key = &self.inbound_payment_key;
inbound_payment::get_payment_preimage(
Expand DownExpand Up@@ -17235,7 +17228,9 @@ impl<
relative_expiry,
None,
None,
).map_err(|_| Bolt12SemanticError::InvalidAmount)
)
.map_err(|_| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
};

let (result, context) = match invoice_request {
Expand DownExpand Up@@ -22137,7 +22132,8 @@ pub mod bench {
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();
let (payment_secret, _no_payment_metadata) =
$node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();

$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000),
PaymentId(payment_hash.0),
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2800,7 +2800,7 @@ pub fn get_payment_preimage_hash(
let payment_preimage = PaymentPreimage([*payment_count; 32]);
*payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = recipient
let (payment_secret, _) = recipient
.node
.create_inbound_payment_for_hash(
payment_hash,
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Encrypt `payment_metadata` when we build the payment secret by TheBlueMatt · Pull Request #4628 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1369,7 +1369,7 @@ impl PaymentTracker {
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&self.payment_ctr.to_be_bytes());
let hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array());
let secret = dest
let (secret, _no_metadata) = dest
.create_inbound_payment_for_hash(hash, None, 3600, None, None)
.expect("create_inbound_payment_for_hash failed");
assert!(self.payment_preimages.insert(hash, payment_preimage).is_none());
Expand Down
2 changes: 1 addition & 1 deletion lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -120,7 +120,7 @@ fn create_jit_invoice(
) -> Result<Bolt11Invoice, ()> {
// LSPS2 requires min_final_cltv_expiry_delta to be at least 2 more than usual.
let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2;
let (payment_hash, payment_secret) = node
let (payment_hash, payment_secret, _) = node
.node
.create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta), None)
.map_err(|e| {
Expand Down
26 changes: 21 additions & 5 deletions lightning/src/crypto/utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::{ecdsa::Signature, Message, Secp256k1, SecretKey, Signing};

use chacha20_poly1305::chacha20::{ChaCha20, Key, Nonce};

use crate::sign::EntropySource;

macro_rules! hkdf_extract_expand {
Expand All@@ -22,7 +24,7 @@ macro_rules! hkdf_extract_expand {
let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
(k1, k2)
}};
($salt: expr, $ikm: expr, 7) => {{
($salt: expr, $ikm: expr, 8) => {{
let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);

let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
Expand DownExpand Up@@ -50,18 +52,23 @@ macro_rules! hkdf_extract_expand {
hmac.input(&[7; 1]);
let k7 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7)
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
hmac.input(&k7);
hmac.input(&[8; 1]);
let k8 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7, k8)
}};
}

pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 2)
}

pub fn hkdf_extract_expand_7x(
pub fn hkdf_extract_expand_8x(
salt: &[u8], ikm: &[u8],
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 7)
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 8)
}

#[inline]
Expand DownExpand Up@@ -91,3 +98,12 @@ pub fn sign_with_aux_rand<C: Signing, ES: EntropySource>(
let sig = sign(ctx, msg, sk);
sig
}

pub fn apply_chacha20(key: [u8; 32], nonce: [u8; 16], data: &mut [u8]) {
ChaCha20::new_from_block(
Key::new(key),
Nonce::new(nonce[4..].try_into().unwrap()),
u32::from_le_bytes(nonce[..4].try_into().unwrap()),
)
.apply_keystream(data);
}
16 changes: 10 additions & 6 deletions lightning/src/ln/bolt11_payment_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,10 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -41,7 +43,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(50_000)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand DownExpand Up@@ -97,8 +99,10 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -107,7 +111,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
.payment_secret(payment_secret)
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand Down
58 changes: 27 additions & 31 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8462,7 +8462,7 @@ impl<
payment_data,
payment_context,
phantom_shared_secret,
onion_fields,
mut onion_fields,
has_recipient_created_payment_secret,
invoice_request_opt,
trampoline_shared_secret,
Expand DownExpand Up@@ -8603,7 +8603,7 @@ impl<
let verify_res = inbound_payment::verify(
payment_hash,
&payment_data,
onion_fields.payment_metadata.as_deref(),
onion_fields.payment_metadata.as_mut(),
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
&self.inbound_payment_key,
&self.logger,
Expand DownExpand Up@@ -14372,24 +14372,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
}
}

let (payment_hash, payment_secret) = match payment_hash {
let (payment_hash, payment_secret, payment_metadata) = match payment_hash {
Some(payment_hash) => {
let payment_secret = self
let (payment_secret, payment_metadata) = self
.create_inbound_payment_for_hash(
payment_hash, amount_msats,
invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
(payment_hash, payment_secret)
(payment_hash, payment_secret, payment_metadata)
},
None => {
self
.create_inbound_payment(
amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
},
Expand DownExpand Up@@ -14516,8 +14516,7 @@ pub struct Bolt11InvoiceParameters {
/// onion by the sender, available as [`RecipientOnionFields::payment_metadata`] via
/// [`Event::PaymentClaimable::onion_fields`].
///
/// Note that because it is exposed to the sender in the invoice you should consider encrypting
/// it. It is committed to, however, so cannot be modified by the sender.
/// The metadata itself is encrypted and HMAC'd before being stored in the BOLT 11 invoice.
pub payment_metadata: Option<Vec<u8>>,
}

Expand DownExpand Up@@ -15023,6 +15022,7 @@ impl<
|amount_msats, relative_expiry| {
self.create_inbound_payment(Some(amount_msats), relative_expiry, None, None)
.map_err(|()| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
},
None,
)?;
Expand All@@ -15033,8 +15033,8 @@ impl<
Ok(invoice)
}

/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
/// to pay us.
/// Gets a payment secret, payment hash, and encrypts the `payment_metadata` for use in an
/// invoice given to a third party wishing to pay us.
///
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
Expand DownExpand Up@@ -15065,8 +15065,8 @@ impl<
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(
&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<&[u8]>,
) -> Result<(PaymentHash, PaymentSecret), ()> {
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentHash, PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create(
&self.inbound_payment_key,
min_value_msat,
Expand All@@ -15078,8 +15078,8 @@ impl<
)
}

/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`] (for which the payment preimage is
/// stored external to LDK) and encrypts the `payment_metadata`.
///
/// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
Expand DownExpand Up@@ -15115,41 +15115,34 @@ impl<
/// Note that a malicious eavesdropper can intuit whether an inbound payment was created by
/// `create_inbound_payment` or `create_inbound_payment_for_hash` based on runtime.
///
/// # Note
///
/// If you register an inbound payment with this method, then serialize the `ChannelManager`, then
/// deserialize it with a node running 0.0.103 and earlier, the payment will fail to be received.
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(
&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>,
payment_metadata: Option<&[u8]>,
) -> Result<PaymentSecret, ()> {
payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create_from_hash(
&self.inbound_payment_key,
min_value_msat,
payment_hash,
invoice_expiry_delta_secs,
&self.entropy_source,
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
min_final_cltv_expiry,
payment_metadata,
)
}

/// Gets an LDK-generated payment preimage from a payment hash, metadata and secret that were
/// previously returned from [`create_inbound_payment`].
/// Gets an LDK-generated payment preimage from a payment hashand secret and decrypts the
/// metadata (if any) that were previously returned from [`create_inbound_payment`].
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
pub fn get_payment_preimage(
pub fn get_payment_preimage_decrypt_metadata(
&self, payment_hash: PaymentHash, payment_secret: PaymentSecret,
payment_metadata: Option<&[u8]>,
payment_metadata: Option<&mut [u8]>,
) -> Result<PaymentPreimage, APIError> {
let expanded_key = &self.inbound_payment_key;
inbound_payment::get_payment_preimage(
Expand DownExpand Up@@ -17235,7 +17228,9 @@ impl<
relative_expiry,
None,
None,
).map_err(|_| Bolt12SemanticError::InvalidAmount)
)
.map_err(|_| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
};

let (result, context) = match invoice_request {
Expand DownExpand Up@@ -22137,7 +22132,8 @@ pub mod bench {
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();
let (payment_secret, _no_payment_metadata) =
$node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();

$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000),
PaymentId(payment_hash.0),
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2800,7 +2800,7 @@ pub fn get_payment_preimage_hash(
let payment_preimage = PaymentPreimage([*payment_count; 32]);
*payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = recipient
let (payment_secret, _) = recipient
.node
.create_inbound_payment_for_hash(
payment_hash,
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Encrypt `payment_metadata` when we build the payment secret by TheBlueMatt · Pull Request #4628 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1369,7 +1369,7 @@ impl PaymentTracker {
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&self.payment_ctr.to_be_bytes());
let hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array());
let secret = dest
let (secret, _no_metadata) = dest
.create_inbound_payment_for_hash(hash, None, 3600, None, None)
.expect("create_inbound_payment_for_hash failed");
assert!(self.payment_preimages.insert(hash, payment_preimage).is_none());
Expand Down
2 changes: 1 addition & 1 deletion lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -120,7 +120,7 @@ fn create_jit_invoice(
) -> Result<Bolt11Invoice, ()> {
// LSPS2 requires min_final_cltv_expiry_delta to be at least 2 more than usual.
let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2;
let (payment_hash, payment_secret) = node
let (payment_hash, payment_secret, _) = node
.node
.create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta), None)
.map_err(|e| {
Expand Down
26 changes: 21 additions & 5 deletions lightning/src/crypto/utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::{ecdsa::Signature, Message, Secp256k1, SecretKey, Signing};

use chacha20_poly1305::chacha20::{ChaCha20, Key, Nonce};

use crate::sign::EntropySource;

macro_rules! hkdf_extract_expand {
Expand All@@ -22,7 +24,7 @@ macro_rules! hkdf_extract_expand {
let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
(k1, k2)
}};
($salt: expr, $ikm: expr, 7) => {{
($salt: expr, $ikm: expr, 8) => {{
let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);

let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
Expand DownExpand Up@@ -50,18 +52,23 @@ macro_rules! hkdf_extract_expand {
hmac.input(&[7; 1]);
let k7 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7)
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
hmac.input(&k7);
hmac.input(&[8; 1]);
let k8 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7, k8)
}};
}

pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 2)
}

pub fn hkdf_extract_expand_7x(
pub fn hkdf_extract_expand_8x(
salt: &[u8], ikm: &[u8],
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 7)
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 8)
}

#[inline]
Expand DownExpand Up@@ -91,3 +98,12 @@ pub fn sign_with_aux_rand<C: Signing, ES: EntropySource>(
let sig = sign(ctx, msg, sk);
sig
}

pub fn apply_chacha20(key: [u8; 32], nonce: [u8; 16], data: &mut [u8]) {
ChaCha20::new_from_block(
Key::new(key),
Nonce::new(nonce[4..].try_into().unwrap()),
u32::from_le_bytes(nonce[..4].try_into().unwrap()),
)
.apply_keystream(data);
}
16 changes: 10 additions & 6 deletions lightning/src/ln/bolt11_payment_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,10 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -41,7 +43,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(50_000)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand DownExpand Up@@ -97,8 +99,10 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -107,7 +111,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
.payment_secret(payment_secret)
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand Down
58 changes: 27 additions & 31 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8462,7 +8462,7 @@ impl<
payment_data,
payment_context,
phantom_shared_secret,
onion_fields,
mut onion_fields,
has_recipient_created_payment_secret,
invoice_request_opt,
trampoline_shared_secret,
Expand DownExpand Up@@ -8603,7 +8603,7 @@ impl<
let verify_res = inbound_payment::verify(
payment_hash,
&payment_data,
onion_fields.payment_metadata.as_deref(),
onion_fields.payment_metadata.as_mut(),
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
&self.inbound_payment_key,
&self.logger,
Expand DownExpand Up@@ -14372,24 +14372,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
}
}

let (payment_hash, payment_secret) = match payment_hash {
let (payment_hash, payment_secret, payment_metadata) = match payment_hash {
Some(payment_hash) => {
let payment_secret = self
let (payment_secret, payment_metadata) = self
.create_inbound_payment_for_hash(
payment_hash, amount_msats,
invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
(payment_hash, payment_secret)
(payment_hash, payment_secret, payment_metadata)
},
None => {
self
.create_inbound_payment(
amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
},
Expand DownExpand Up@@ -14516,8 +14516,7 @@ pub struct Bolt11InvoiceParameters {
/// onion by the sender, available as [`RecipientOnionFields::payment_metadata`] via
/// [`Event::PaymentClaimable::onion_fields`].
///
/// Note that because it is exposed to the sender in the invoice you should consider encrypting
/// it. It is committed to, however, so cannot be modified by the sender.
/// The metadata itself is encrypted and HMAC'd before being stored in the BOLT 11 invoice.
pub payment_metadata: Option<Vec<u8>>,
}

Expand DownExpand Up@@ -15023,6 +15022,7 @@ impl<
|amount_msats, relative_expiry| {
self.create_inbound_payment(Some(amount_msats), relative_expiry, None, None)
.map_err(|()| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
},
None,
)?;
Expand All@@ -15033,8 +15033,8 @@ impl<
Ok(invoice)
}

/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
/// to pay us.
/// Gets a payment secret, payment hash, and encrypts the `payment_metadata` for use in an
/// invoice given to a third party wishing to pay us.
///
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
Expand DownExpand Up@@ -15065,8 +15065,8 @@ impl<
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(
&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<&[u8]>,
) -> Result<(PaymentHash, PaymentSecret), ()> {
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentHash, PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create(
&self.inbound_payment_key,
min_value_msat,
Expand All@@ -15078,8 +15078,8 @@ impl<
)
}

/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`] (for which the payment preimage is
/// stored external to LDK) and encrypts the `payment_metadata`.
///
/// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
Expand DownExpand Up@@ -15115,41 +15115,34 @@ impl<
/// Note that a malicious eavesdropper can intuit whether an inbound payment was created by
/// `create_inbound_payment` or `create_inbound_payment_for_hash` based on runtime.
///
/// # Note
///
/// If you register an inbound payment with this method, then serialize the `ChannelManager`, then
/// deserialize it with a node running 0.0.103 and earlier, the payment will fail to be received.
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(
&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>,
payment_metadata: Option<&[u8]>,
) -> Result<PaymentSecret, ()> {
payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create_from_hash(
&self.inbound_payment_key,
min_value_msat,
payment_hash,
invoice_expiry_delta_secs,
&self.entropy_source,
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
min_final_cltv_expiry,
payment_metadata,
)
}

/// Gets an LDK-generated payment preimage from a payment hash, metadata and secret that were
/// previously returned from [`create_inbound_payment`].
/// Gets an LDK-generated payment preimage from a payment hashand secret and decrypts the
/// metadata (if any) that were previously returned from [`create_inbound_payment`].
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
pub fn get_payment_preimage(
pub fn get_payment_preimage_decrypt_metadata(
&self, payment_hash: PaymentHash, payment_secret: PaymentSecret,
payment_metadata: Option<&[u8]>,
payment_metadata: Option<&mut [u8]>,
) -> Result<PaymentPreimage, APIError> {
let expanded_key = &self.inbound_payment_key;
inbound_payment::get_payment_preimage(
Expand DownExpand Up@@ -17235,7 +17228,9 @@ impl<
relative_expiry,
None,
None,
).map_err(|_| Bolt12SemanticError::InvalidAmount)
)
.map_err(|_| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
};

let (result, context) = match invoice_request {
Expand DownExpand Up@@ -22137,7 +22132,8 @@ pub mod bench {
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();
let (payment_secret, _no_payment_metadata) =
$node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();

$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000),
PaymentId(payment_hash.0),
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2800,7 +2800,7 @@ pub fn get_payment_preimage_hash(
let payment_preimage = PaymentPreimage([*payment_count; 32]);
*payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = recipient
let (payment_secret, _) = recipient
.node
.create_inbound_payment_for_hash(
payment_hash,
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Encrypt `payment_metadata` when we build the payment secret by TheBlueMatt · Pull Request #4628 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1369,7 +1369,7 @@ impl PaymentTracker {
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&self.payment_ctr.to_be_bytes());
let hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array());
let secret = dest
let (secret, _no_metadata) = dest
.create_inbound_payment_for_hash(hash, None, 3600, None, None)
.expect("create_inbound_payment_for_hash failed");
assert!(self.payment_preimages.insert(hash, payment_preimage).is_none());
Expand Down
2 changes: 1 addition & 1 deletion lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -120,7 +120,7 @@ fn create_jit_invoice(
) -> Result<Bolt11Invoice, ()> {
// LSPS2 requires min_final_cltv_expiry_delta to be at least 2 more than usual.
let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2;
let (payment_hash, payment_secret) = node
let (payment_hash, payment_secret, _) = node
.node
.create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta), None)
.map_err(|e| {
Expand Down
26 changes: 21 additions & 5 deletions lightning/src/crypto/utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::{ecdsa::Signature, Message, Secp256k1, SecretKey, Signing};

use chacha20_poly1305::chacha20::{ChaCha20, Key, Nonce};

use crate::sign::EntropySource;

macro_rules! hkdf_extract_expand {
Expand All@@ -22,7 +24,7 @@ macro_rules! hkdf_extract_expand {
let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
(k1, k2)
}};
($salt: expr, $ikm: expr, 7) => {{
($salt: expr, $ikm: expr, 8) => {{
let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);

let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
Expand DownExpand Up@@ -50,18 +52,23 @@ macro_rules! hkdf_extract_expand {
hmac.input(&[7; 1]);
let k7 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7)
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
hmac.input(&k7);
hmac.input(&[8; 1]);
let k8 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7, k8)
}};
}

pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 2)
}

pub fn hkdf_extract_expand_7x(
pub fn hkdf_extract_expand_8x(
salt: &[u8], ikm: &[u8],
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 7)
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 8)
}

#[inline]
Expand DownExpand Up@@ -91,3 +98,12 @@ pub fn sign_with_aux_rand<C: Signing, ES: EntropySource>(
let sig = sign(ctx, msg, sk);
sig
}

pub fn apply_chacha20(key: [u8; 32], nonce: [u8; 16], data: &mut [u8]) {
ChaCha20::new_from_block(
Key::new(key),
Nonce::new(nonce[4..].try_into().unwrap()),
u32::from_le_bytes(nonce[..4].try_into().unwrap()),
)
.apply_keystream(data);
}
16 changes: 10 additions & 6 deletions lightning/src/ln/bolt11_payment_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,10 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -41,7 +43,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(50_000)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand DownExpand Up@@ -97,8 +99,10 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -107,7 +111,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
.payment_secret(payment_secret)
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand Down
58 changes: 27 additions & 31 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8462,7 +8462,7 @@ impl<
payment_data,
payment_context,
phantom_shared_secret,
onion_fields,
mut onion_fields,
has_recipient_created_payment_secret,
invoice_request_opt,
trampoline_shared_secret,
Expand DownExpand Up@@ -8603,7 +8603,7 @@ impl<
let verify_res = inbound_payment::verify(
payment_hash,
&payment_data,
onion_fields.payment_metadata.as_deref(),
onion_fields.payment_metadata.as_mut(),
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
&self.inbound_payment_key,
&self.logger,
Expand DownExpand Up@@ -14372,24 +14372,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
}
}

let (payment_hash, payment_secret) = match payment_hash {
let (payment_hash, payment_secret, payment_metadata) = match payment_hash {
Some(payment_hash) => {
let payment_secret = self
let (payment_secret, payment_metadata) = self
.create_inbound_payment_for_hash(
payment_hash, amount_msats,
invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
(payment_hash, payment_secret)
(payment_hash, payment_secret, payment_metadata)
},
None => {
self
.create_inbound_payment(
amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
},
Expand DownExpand Up@@ -14516,8 +14516,7 @@ pub struct Bolt11InvoiceParameters {
/// onion by the sender, available as [`RecipientOnionFields::payment_metadata`] via
/// [`Event::PaymentClaimable::onion_fields`].
///
/// Note that because it is exposed to the sender in the invoice you should consider encrypting
/// it. It is committed to, however, so cannot be modified by the sender.
/// The metadata itself is encrypted and HMAC'd before being stored in the BOLT 11 invoice.
pub payment_metadata: Option<Vec<u8>>,
}

Expand DownExpand Up@@ -15023,6 +15022,7 @@ impl<
|amount_msats, relative_expiry| {
self.create_inbound_payment(Some(amount_msats), relative_expiry, None, None)
.map_err(|()| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
},
None,
)?;
Expand All@@ -15033,8 +15033,8 @@ impl<
Ok(invoice)
}

/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
/// to pay us.
/// Gets a payment secret, payment hash, and encrypts the `payment_metadata` for use in an
/// invoice given to a third party wishing to pay us.
///
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
Expand DownExpand Up@@ -15065,8 +15065,8 @@ impl<
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(
&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<&[u8]>,
) -> Result<(PaymentHash, PaymentSecret), ()> {
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentHash, PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create(
&self.inbound_payment_key,
min_value_msat,
Expand All@@ -15078,8 +15078,8 @@ impl<
)
}

/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`] (for which the payment preimage is
/// stored external to LDK) and encrypts the `payment_metadata`.
///
/// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
Expand DownExpand Up@@ -15115,41 +15115,34 @@ impl<
/// Note that a malicious eavesdropper can intuit whether an inbound payment was created by
/// `create_inbound_payment` or `create_inbound_payment_for_hash` based on runtime.
///
/// # Note
///
/// If you register an inbound payment with this method, then serialize the `ChannelManager`, then
/// deserialize it with a node running 0.0.103 and earlier, the payment will fail to be received.
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(
&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>,
payment_metadata: Option<&[u8]>,
) -> Result<PaymentSecret, ()> {
payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create_from_hash(
&self.inbound_payment_key,
min_value_msat,
payment_hash,
invoice_expiry_delta_secs,
&self.entropy_source,
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
min_final_cltv_expiry,
payment_metadata,
)
}

/// Gets an LDK-generated payment preimage from a payment hash, metadata and secret that were
/// previously returned from [`create_inbound_payment`].
/// Gets an LDK-generated payment preimage from a payment hashand secret and decrypts the
/// metadata (if any) that were previously returned from [`create_inbound_payment`].
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
pub fn get_payment_preimage(
pub fn get_payment_preimage_decrypt_metadata(
&self, payment_hash: PaymentHash, payment_secret: PaymentSecret,
payment_metadata: Option<&[u8]>,
payment_metadata: Option<&mut [u8]>,
) -> Result<PaymentPreimage, APIError> {
let expanded_key = &self.inbound_payment_key;
inbound_payment::get_payment_preimage(
Expand DownExpand Up@@ -17235,7 +17228,9 @@ impl<
relative_expiry,
None,
None,
).map_err(|_| Bolt12SemanticError::InvalidAmount)
)
.map_err(|_| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
};

let (result, context) = match invoice_request {
Expand DownExpand Up@@ -22137,7 +22132,8 @@ pub mod bench {
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();
let (payment_secret, _no_payment_metadata) =
$node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();

$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000),
PaymentId(payment_hash.0),
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2800,7 +2800,7 @@ pub fn get_payment_preimage_hash(
let payment_preimage = PaymentPreimage([*payment_count; 32]);
*payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = recipient
let (payment_secret, _) = recipient
.node
.create_inbound_payment_for_hash(
payment_hash,
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Encrypt `payment_metadata` when we build the payment secret by TheBlueMatt · Pull Request #4628 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1369,7 +1369,7 @@ impl PaymentTracker {
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&self.payment_ctr.to_be_bytes());
let hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array());
let secret = dest
let (secret, _no_metadata) = dest
.create_inbound_payment_for_hash(hash, None, 3600, None, None)
.expect("create_inbound_payment_for_hash failed");
assert!(self.payment_preimages.insert(hash, payment_preimage).is_none());
Expand Down
2 changes: 1 addition & 1 deletion lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -120,7 +120,7 @@ fn create_jit_invoice(
) -> Result<Bolt11Invoice, ()> {
// LSPS2 requires min_final_cltv_expiry_delta to be at least 2 more than usual.
let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2;
let (payment_hash, payment_secret) = node
let (payment_hash, payment_secret, _) = node
.node
.create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta), None)
.map_err(|e| {
Expand Down
26 changes: 21 additions & 5 deletions lightning/src/crypto/utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::{ecdsa::Signature, Message, Secp256k1, SecretKey, Signing};

use chacha20_poly1305::chacha20::{ChaCha20, Key, Nonce};

use crate::sign::EntropySource;

macro_rules! hkdf_extract_expand {
Expand All@@ -22,7 +24,7 @@ macro_rules! hkdf_extract_expand {
let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
(k1, k2)
}};
($salt: expr, $ikm: expr, 7) => {{
($salt: expr, $ikm: expr, 8) => {{
let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);

let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
Expand DownExpand Up@@ -50,18 +52,23 @@ macro_rules! hkdf_extract_expand {
hmac.input(&[7; 1]);
let k7 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7)
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
hmac.input(&k7);
hmac.input(&[8; 1]);
let k8 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7, k8)
}};
}

pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 2)
}

pub fn hkdf_extract_expand_7x(
pub fn hkdf_extract_expand_8x(
salt: &[u8], ikm: &[u8],
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 7)
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 8)
}

#[inline]
Expand DownExpand Up@@ -91,3 +98,12 @@ pub fn sign_with_aux_rand<C: Signing, ES: EntropySource>(
let sig = sign(ctx, msg, sk);
sig
}

pub fn apply_chacha20(key: [u8; 32], nonce: [u8; 16], data: &mut [u8]) {
ChaCha20::new_from_block(
Key::new(key),
Nonce::new(nonce[4..].try_into().unwrap()),
u32::from_le_bytes(nonce[..4].try_into().unwrap()),
)
.apply_keystream(data);
}
16 changes: 10 additions & 6 deletions lightning/src/ln/bolt11_payment_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,10 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -41,7 +43,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(50_000)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand DownExpand Up@@ -97,8 +99,10 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -107,7 +111,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
.payment_secret(payment_secret)
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand Down
58 changes: 27 additions & 31 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8462,7 +8462,7 @@ impl<
payment_data,
payment_context,
phantom_shared_secret,
onion_fields,
mut onion_fields,
has_recipient_created_payment_secret,
invoice_request_opt,
trampoline_shared_secret,
Expand DownExpand Up@@ -8603,7 +8603,7 @@ impl<
let verify_res = inbound_payment::verify(
payment_hash,
&payment_data,
onion_fields.payment_metadata.as_deref(),
onion_fields.payment_metadata.as_mut(),
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
&self.inbound_payment_key,
&self.logger,
Expand DownExpand Up@@ -14372,24 +14372,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
}
}

let (payment_hash, payment_secret) = match payment_hash {
let (payment_hash, payment_secret, payment_metadata) = match payment_hash {
Some(payment_hash) => {
let payment_secret = self
let (payment_secret, payment_metadata) = self
.create_inbound_payment_for_hash(
payment_hash, amount_msats,
invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
(payment_hash, payment_secret)
(payment_hash, payment_secret, payment_metadata)
},
None => {
self
.create_inbound_payment(
amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
},
Expand DownExpand Up@@ -14516,8 +14516,7 @@ pub struct Bolt11InvoiceParameters {
/// onion by the sender, available as [`RecipientOnionFields::payment_metadata`] via
/// [`Event::PaymentClaimable::onion_fields`].
///
/// Note that because it is exposed to the sender in the invoice you should consider encrypting
/// it. It is committed to, however, so cannot be modified by the sender.
/// The metadata itself is encrypted and HMAC'd before being stored in the BOLT 11 invoice.
pub payment_metadata: Option<Vec<u8>>,
}

Expand DownExpand Up@@ -15023,6 +15022,7 @@ impl<
|amount_msats, relative_expiry| {
self.create_inbound_payment(Some(amount_msats), relative_expiry, None, None)
.map_err(|()| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
},
None,
)?;
Expand All@@ -15033,8 +15033,8 @@ impl<
Ok(invoice)
}

/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
/// to pay us.
/// Gets a payment secret, payment hash, and encrypts the `payment_metadata` for use in an
/// invoice given to a third party wishing to pay us.
///
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
Expand DownExpand Up@@ -15065,8 +15065,8 @@ impl<
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(
&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<&[u8]>,
) -> Result<(PaymentHash, PaymentSecret), ()> {
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentHash, PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create(
&self.inbound_payment_key,
min_value_msat,
Expand All@@ -15078,8 +15078,8 @@ impl<
)
}

/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`] (for which the payment preimage is
/// stored external to LDK) and encrypts the `payment_metadata`.
///
/// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
Expand DownExpand Up@@ -15115,41 +15115,34 @@ impl<
/// Note that a malicious eavesdropper can intuit whether an inbound payment was created by
/// `create_inbound_payment` or `create_inbound_payment_for_hash` based on runtime.
///
/// # Note
///
/// If you register an inbound payment with this method, then serialize the `ChannelManager`, then
/// deserialize it with a node running 0.0.103 and earlier, the payment will fail to be received.
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(
&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>,
payment_metadata: Option<&[u8]>,
) -> Result<PaymentSecret, ()> {
payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create_from_hash(
&self.inbound_payment_key,
min_value_msat,
payment_hash,
invoice_expiry_delta_secs,
&self.entropy_source,
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
min_final_cltv_expiry,
payment_metadata,
)
}

/// Gets an LDK-generated payment preimage from a payment hash, metadata and secret that were
/// previously returned from [`create_inbound_payment`].
/// Gets an LDK-generated payment preimage from a payment hashand secret and decrypts the
/// metadata (if any) that were previously returned from [`create_inbound_payment`].
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
pub fn get_payment_preimage(
pub fn get_payment_preimage_decrypt_metadata(
&self, payment_hash: PaymentHash, payment_secret: PaymentSecret,
payment_metadata: Option<&[u8]>,
payment_metadata: Option<&mut [u8]>,
) -> Result<PaymentPreimage, APIError> {
let expanded_key = &self.inbound_payment_key;
inbound_payment::get_payment_preimage(
Expand DownExpand Up@@ -17235,7 +17228,9 @@ impl<
relative_expiry,
None,
None,
).map_err(|_| Bolt12SemanticError::InvalidAmount)
)
.map_err(|_| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
};

let (result, context) = match invoice_request {
Expand DownExpand Up@@ -22137,7 +22132,8 @@ pub mod bench {
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();
let (payment_secret, _no_payment_metadata) =
$node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();

$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000),
PaymentId(payment_hash.0),
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2800,7 +2800,7 @@ pub fn get_payment_preimage_hash(
let payment_preimage = PaymentPreimage([*payment_count; 32]);
*payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = recipient
let (payment_secret, _) = recipient
.node
.create_inbound_payment_for_hash(
payment_hash,
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Encrypt `payment_metadata` when we build the payment secret by TheBlueMatt · Pull Request #4628 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1369,7 +1369,7 @@ impl PaymentTracker {
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&self.payment_ctr.to_be_bytes());
let hash = PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array());
let secret = dest
let (secret, _no_metadata) = dest
.create_inbound_payment_for_hash(hash, None, 3600, None, None)
.expect("create_inbound_payment_for_hash failed");
assert!(self.payment_preimages.insert(hash, payment_preimage).is_none());
Expand Down
2 changes: 1 addition & 1 deletion lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -120,7 +120,7 @@ fn create_jit_invoice(
) -> Result<Bolt11Invoice, ()> {
// LSPS2 requires min_final_cltv_expiry_delta to be at least 2 more than usual.
let min_final_cltv_expiry_delta = MIN_FINAL_CLTV_EXPIRY_DELTA + 2;
let (payment_hash, payment_secret) = node
let (payment_hash, payment_secret, _) = node
.node
.create_inbound_payment(None, expiry_secs, Some(min_final_cltv_expiry_delta), None)
.map_err(|e| {
Expand Down
26 changes: 21 additions & 5 deletions lightning/src/crypto/utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,8 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::secp256k1::{ecdsa::Signature, Message, Secp256k1, SecretKey, Signing};

use chacha20_poly1305::chacha20::{ChaCha20, Key, Nonce};

use crate::sign::EntropySource;

macro_rules! hkdf_extract_expand {
Expand All@@ -22,7 +24,7 @@ macro_rules! hkdf_extract_expand {
let (k1, k2, _) = hkdf_extract_expand!($salt, $ikm);
(k1, k2)
}};
($salt: expr, $ikm: expr, 7) => {{
($salt: expr, $ikm: expr, 8) => {{
let (k1, k2, prk) = hkdf_extract_expand!($salt, $ikm);

let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
Expand DownExpand Up@@ -50,18 +52,23 @@ macro_rules! hkdf_extract_expand {
hmac.input(&[7; 1]);
let k7 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7)
let mut hmac = HmacEngine::<Sha256>::new(&prk[..]);
hmac.input(&k7);
hmac.input(&[8; 1]);
let k8 = Hmac::from_engine(hmac).to_byte_array();

(k1, k2, k3, k4, k5, k6, k7, k8)
}};
}

pub fn hkdf_extract_expand_twice(salt: &[u8], ikm: &[u8]) -> ([u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 2)
}

pub fn hkdf_extract_expand_7x(
pub fn hkdf_extract_expand_8x(
salt: &[u8], ikm: &[u8],
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 7)
) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
hkdf_extract_expand!(salt, ikm, 8)
}

#[inline]
Expand DownExpand Up@@ -91,3 +98,12 @@ pub fn sign_with_aux_rand<C: Signing, ES: EntropySource>(
let sig = sign(ctx, msg, sk);
sig
}

pub fn apply_chacha20(key: [u8; 32], nonce: [u8; 16], data: &mut [u8]) {
ChaCha20::new_from_block(
Key::new(key),
Nonce::new(nonce[4..].try_into().unwrap()),
u32::from_le_bytes(nonce[..4].try_into().unwrap()),
)
.apply_keystream(data);
}
16 changes: 10 additions & 6 deletions lightning/src/ln/bolt11_payment_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,8 +30,10 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -41,7 +43,7 @@ fn payment_metadata_end_to_end_for_invoice_with_amount() {
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.amount_milli_satoshis(50_000)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand DownExpand Up@@ -97,8 +99,10 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {

let payment_metadata = vec![42, 43, 44, 45, 46, 47, 48, 49, 42];

let (payment_hash, payment_secret) =
nodes[1].node.create_inbound_payment(None, 7200, None, Some(&payment_metadata)).unwrap();
let (payment_hash, payment_secret, encrypted_metadata) = nodes[1]
.node
.create_inbound_payment(None, 7200, None, Some(payment_metadata.clone()))
.unwrap();

let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let invoice = InvoiceBuilder::new(Currency::Bitcoin)
Expand All@@ -107,7 +111,7 @@ fn payment_metadata_end_to_end_for_invoice_with_no_amount() {
.payment_secret(payment_secret)
.duration_since_epoch(timestamp)
.min_final_cltv_expiry_delta(144)
.payment_metadata(payment_metadata.clone())
.payment_metadata(encrypted_metadata.unwrap())
.build_raw()
.unwrap();
let sig = nodes[1].keys_manager.backing.sign_invoice(&invoice, Recipient::Node).unwrap();
Expand Down
58 changes: 27 additions & 31 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -8462,7 +8462,7 @@ impl<
payment_data,
payment_context,
phantom_shared_secret,
onion_fields,
mut onion_fields,
has_recipient_created_payment_secret,
invoice_request_opt,
trampoline_shared_secret,
Expand DownExpand Up@@ -8603,7 +8603,7 @@ impl<
let verify_res = inbound_payment::verify(
payment_hash,
&payment_data,
onion_fields.payment_metadata.as_deref(),
onion_fields.payment_metadata.as_mut(),
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
&self.inbound_payment_key,
&self.logger,
Expand DownExpand Up@@ -14372,24 +14372,24 @@ This indicates a bug inside LDK. Please report this error at https://github.com/
}
}

let (payment_hash, payment_secret) = match payment_hash {
let (payment_hash, payment_secret, payment_metadata) = match payment_hash {
Some(payment_hash) => {
let payment_secret = self
let (payment_secret, payment_metadata) = self
.create_inbound_payment_for_hash(
payment_hash, amount_msats,
invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
(payment_hash, payment_secret)
(payment_hash, payment_secret, payment_metadata)
},
None => {
self
.create_inbound_payment(
amount_msats, invoice_expiry_delta_secs.unwrap_or(DEFAULT_EXPIRY_TIME as u32),
min_final_cltv_expiry_delta,
payment_metadata.as_deref(),
payment_metadata,
)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?
},
Expand DownExpand Up@@ -14516,8 +14516,7 @@ pub struct Bolt11InvoiceParameters {
/// onion by the sender, available as [`RecipientOnionFields::payment_metadata`] via
/// [`Event::PaymentClaimable::onion_fields`].
///
/// Note that because it is exposed to the sender in the invoice you should consider encrypting
/// it. It is committed to, however, so cannot be modified by the sender.
/// The metadata itself is encrypted and HMAC'd before being stored in the BOLT 11 invoice.
pub payment_metadata: Option<Vec<u8>>,
}

Expand DownExpand Up@@ -15023,6 +15022,7 @@ impl<
|amount_msats, relative_expiry| {
self.create_inbound_payment(Some(amount_msats), relative_expiry, None, None)
.map_err(|()| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
},
None,
)?;
Expand All@@ -15033,8 +15033,8 @@ impl<
Ok(invoice)
}

/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
/// to pay us.
/// Gets a payment secret, payment hash, and encrypts the `payment_metadata` for use in an
/// invoice given to a third party wishing to pay us.
///
/// This differs from [`create_inbound_payment_for_hash`] only in that it generates the
/// [`PaymentHash`] and [`PaymentPreimage`] for you.
Expand DownExpand Up@@ -15065,8 +15065,8 @@ impl<
/// [`create_inbound_payment_for_hash`]: Self::create_inbound_payment_for_hash
pub fn create_inbound_payment(
&self, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32,
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<&[u8]>,
) -> Result<(PaymentHash, PaymentSecret), ()> {
min_final_cltv_expiry_delta: Option<u16>, payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentHash, PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create(
&self.inbound_payment_key,
min_value_msat,
Expand All@@ -15078,8 +15078,8 @@ impl<
)
}

/// Gets a [`PaymentSecret`] for a given [`PaymentHash`], for which the payment preimage is
/// stored external to LDK.
/// Gets a [`PaymentSecret`] for a given [`PaymentHash`] (for which the payment preimage is
/// stored external to LDK) and encrypts the `payment_metadata`.
///
/// A [`PaymentClaimable`] event will only be generated if the [`PaymentSecret`] matches a
/// payment secret fetched via this method or [`create_inbound_payment`], and which is at least
Expand DownExpand Up@@ -15115,41 +15115,34 @@ impl<
/// Note that a malicious eavesdropper can intuit whether an inbound payment was created by
/// `create_inbound_payment` or `create_inbound_payment_for_hash` based on runtime.
///
/// # Note
///
/// If you register an inbound payment with this method, then serialize the `ChannelManager`, then
/// deserialize it with a node running 0.0.103 and earlier, the payment will fail to be received.
///
/// Errors if `min_value_msat` is greater than total bitcoin supply.
///
/// If `min_final_cltv_expiry_delta` is set to some value, then the payment will not be receivable
/// on versions of LDK prior to 0.0.114.
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
/// [`PaymentClaimable`]: events::Event::PaymentClaimable
pub fn create_inbound_payment_for_hash(
&self, payment_hash: PaymentHash, min_value_msat: Option<u64>,
invoice_expiry_delta_secs: u32, min_final_cltv_expiry: Option<u16>,
payment_metadata: Option<&[u8]>,
) -> Result<PaymentSecret, ()> {
payment_metadata: Option<Vec<u8>>,
) -> Result<(PaymentSecret, Option<Vec<u8>>), ()> {
inbound_payment::create_from_hash(
&self.inbound_payment_key,
min_value_msat,
payment_hash,
invoice_expiry_delta_secs,
&self.entropy_source,
self.highest_seen_timestamp.load(Ordering::Acquire) as u64,
min_final_cltv_expiry,
payment_metadata,
)
}

/// Gets an LDK-generated payment preimage from a payment hash, metadata and secret that were
/// previously returned from [`create_inbound_payment`].
/// Gets an LDK-generated payment preimage from a payment hashand secret and decrypts the
/// metadata (if any) that were previously returned from [`create_inbound_payment`].
///
/// [`create_inbound_payment`]: Self::create_inbound_payment
pub fn get_payment_preimage(
pub fn get_payment_preimage_decrypt_metadata(
&self, payment_hash: PaymentHash, payment_secret: PaymentSecret,
payment_metadata: Option<&[u8]>,
payment_metadata: Option<&mut [u8]>,
) -> Result<PaymentPreimage, APIError> {
let expanded_key = &self.inbound_payment_key;
inbound_payment::get_payment_preimage(
Expand DownExpand Up@@ -17235,7 +17228,9 @@ impl<
relative_expiry,
None,
None,
).map_err(|_| Bolt12SemanticError::InvalidAmount)
)
.map_err(|_| Bolt12SemanticError::InvalidAmount)
.map(|(preimage, secret, _no_metadata)| (preimage, secret))
};

let (result, context) = match invoice_request {
Expand DownExpand Up@@ -22137,7 +22132,8 @@ pub mod bench {
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();
let (payment_secret, _no_payment_metadata) =
$node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None, None).unwrap();

$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret, 10_000),
PaymentId(payment_hash.0),
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2800,7 +2800,7 @@ pub fn get_payment_preimage_hash(
let payment_preimage = PaymentPreimage([*payment_count; 32]);
*payment_count += 1;
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array());
let payment_secret = recipient
let (payment_secret, _) = recipient
.node
.create_inbound_payment_for_hash(
payment_hash,
Expand Down
Loading
Loading