Skip to content

Commit 5b8972f

Browse files
committed
Split InvoiceRequest::verify_and_respond_using_derived_keys
InvoiceRequest::verify_and_respond_using_derived_keys takes a payment hash. To avoid generating one for invoice requests that ultimately cannot be verified, split the method into one for verifying and another for responding.
1 parent da70db6 commit 5b8972f

File tree

2 files changed

+91
-49
lines changed

2 files changed

+91
-49
lines changed

lightning/src/offers/invoice.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,36 +1240,31 @@ mod tests {
12401240
.build().unwrap()
12411241
.sign(payer_sign).unwrap();
12421242

1243-
if let Err(e) = invoice_request
1244-
.verify_and_respond_using_derived_keys_no_std(
1245-
payment_paths(), payment_hash(), now(), &expanded_key, &secp_ctx
1246-
)
1247-
.unwrap()
1243+
if let Err(e) = invoice_request.clone()
1244+
.verify(&expanded_key, &secp_ctx).unwrap()
1245+
.respond_using_derived_keys_no_std(payment_paths(), payment_hash(), now()).unwrap()
12481246
.build_and_sign(&secp_ctx)
12491247
{
12501248
panic!("error building invoice: {:?}", e);
12511249
}
12521250

12531251
let expanded_key = ExpandedKey::new(&KeyMaterial([41; 32]));
1254-
match invoice_request.verify_and_respond_using_derived_keys_no_std(
1255-
payment_paths(), payment_hash(), now(), &expanded_key, &secp_ctx
1256-
) {
1257-
Ok(_) => panic!("expected error"),
1258-
Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidMetadata),
1259-
}
1252+
assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_err());
12601253

12611254
let desc = "foo".to_string();
12621255
let offer = OfferBuilder
12631256
::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
12641257
.amount_msats(1000)
1258+
// Omit the path so that node_id is used for the signing pubkey instead of deriving
12651259
.build().unwrap();
12661260
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
12671261
.build().unwrap()
12681262
.sign(payer_sign).unwrap();
12691263

1270-
match invoice_request.verify_and_respond_using_derived_keys_no_std(
1271-
payment_paths(), payment_hash(), now(), &expanded_key, &secp_ctx
1272-
) {
1264+
match invoice_request
1265+
.verify(&expanded_key, &secp_ctx).unwrap()
1266+
.respond_using_derived_keys_no_std(payment_paths(), payment_hash(), now())
1267+
{
12731268
Ok(_) => panic!("expected error"),
12741269
Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidMetadata),
12751270
}

lightning/src/offers/invoice_request.rs

Lines changed: 82 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,22 @@ pub struct InvoiceRequest {
398398
signature: Signature,
399399
}
400400

401+
/// An [`InvoiceRequest`] that has been verified by [`InvoiceRequest::verify`] and exposes different
402+
/// ways to respond depending on whether the signing keys were derived.
403+
#[derive(Clone, Debug)]
404+
pub struct VerifiedInvoiceRequest {
405+
/// The verified request.
406+
pub inner: InvoiceRequest,
407+
408+
/// Keys used for signing a [`Bolt12Invoice`] if they can be derived.
409+
///
410+
/// If `Some`, then must respond with methods that use derived keys. Otherwise, should respond
411+
/// with an invoice signed explicitly.
412+
///
413+
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
414+
pub keys: Option<KeyPair>,
415+
}
416+
401417
/// The contents of an [`InvoiceRequest`], which may be shared with an [`Bolt12Invoice`].
402418
///
403419
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
@@ -521,6 +537,60 @@ impl InvoiceRequest {
521537
InvoiceBuilder::for_offer(self, payment_paths, created_at, payment_hash)
522538
}
523539

540+
/// Verifies that the request was for an offer created using the given key. Returns the verified
541+
/// request along with the derived keys needed to sign a [`Bolt12Invoice`] for the request if
542+
/// they could be extracted from the metadata.
543+
///
544+
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
545+
pub fn verify<T: secp256k1::Signing>(
546+
self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
547+
) -> Result<VerifiedInvoiceRequest, ()> {
548+
let keys = self.contents.inner.offer.verify(&self.bytes, key, secp_ctx)?;
549+
Ok(VerifiedInvoiceRequest {
550+
inner: self,
551+
keys,
552+
})
553+
}
554+
555+
#[cfg(test)]
556+
fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef {
557+
let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) =
558+
self.contents.as_tlv_stream();
559+
let signature_tlv_stream = SignatureTlvStreamRef {
560+
signature: Some(&self.signature),
561+
};
562+
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream)
563+
}
564+
}
565+
566+
impl VerifiedInvoiceRequest {
567+
/// Creates an [`InvoiceBuilder`] for the request with the given required fields and using the
568+
/// [`Duration`] since [`std::time::SystemTime::UNIX_EPOCH`] as the creation time.
569+
///
570+
/// See [`InvoiceRequest::respond_with_no_std`] for further details.
571+
///
572+
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
573+
///
574+
/// [`Duration`]: core::time::Duration
575+
#[cfg(feature = "std")]
576+
pub fn respond_with(
577+
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash
578+
) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
579+
self.inner.respond_with(payment_paths, payment_hash)
580+
}
581+
582+
/// Creates an [`InvoiceBuilder`] for the request with the given required fields.
583+
///
584+
/// See [`InvoiceRequest::respond_with_no_std`] for further details.
585+
///
586+
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
587+
pub fn respond_with_no_std(
588+
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
589+
created_at: core::time::Duration
590+
) -> Result<InvoiceBuilder<ExplicitSigningPubkey>, Bolt12SemanticError> {
591+
self.inner.respond_with_no_std(payment_paths, payment_hash, created_at)
592+
}
593+
524594
/// Creates an [`InvoiceBuilder`] for the request using the given required fields and that uses
525595
/// derived signing keys from the originating [`Offer`] to sign the [`Bolt12Invoice`]. Must use
526596
/// the same [`ExpandedKey`] as the one used to create the offer.
@@ -531,17 +601,14 @@ impl InvoiceRequest {
531601
///
532602
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
533603
#[cfg(feature = "std")]
534-
pub fn verify_and_respond_using_derived_keys<T: secp256k1::Signing>(
535-
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
536-
expanded_key: &ExpandedKey, secp_ctx: &Secp256k1<T>
604+
pub fn respond_using_derived_keys(
605+
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash
537606
) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError> {
538607
let created_at = std::time::SystemTime::now()
539608
.duration_since(std::time::SystemTime::UNIX_EPOCH)
540609
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
541610

542-
self.verify_and_respond_using_derived_keys_no_std(
543-
payment_paths, payment_hash, created_at, expanded_key, secp_ctx
544-
)
611+
self.respond_using_derived_keys_no_std(payment_paths, payment_hash, created_at)
545612
}
546613

547614
/// Creates an [`InvoiceBuilder`] for the request using the given required fields and that uses
@@ -553,42 +620,22 @@ impl InvoiceRequest {
553620
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
554621
///
555622
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
556-
pub fn verify_and_respond_using_derived_keys_no_std<T: secp256k1::Signing>(
623+
pub fn respond_using_derived_keys_no_std(
557624
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
558-
created_at: core::time::Duration, expanded_key: &ExpandedKey, secp_ctx: &Secp256k1<T>
625+
created_at: core::time::Duration
559626
) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError> {
560-
if self.features().requires_unknown_bits() {
627+
if self.inner.features().requires_unknown_bits() {
561628
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
562629
}
563630

564-
let keys = match self.verify(expanded_key, secp_ctx) {
565-
Err(()) => return Err(Bolt12SemanticError::InvalidMetadata),
566-
Ok(None) => return Err(Bolt12SemanticError::InvalidMetadata),
567-
Ok(Some(keys)) => keys,
631+
let keys = match self.keys {
632+
None => return Err(Bolt12SemanticError::InvalidMetadata),
633+
Some(keys) => keys,
568634
};
569635

570-
InvoiceBuilder::for_offer_using_keys(self, payment_paths, created_at, payment_hash, keys)
571-
}
572-
573-
/// Verifies that the request was for an offer created using the given key. Returns the derived
574-
/// keys need to sign an [`Bolt12Invoice`] for the request if they could be extracted from the
575-
/// metadata.
576-
///
577-
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
578-
pub fn verify<T: secp256k1::Signing>(
579-
&self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
580-
) -> Result<Option<KeyPair>, ()> {
581-
self.contents.inner.offer.verify(&self.bytes, key, secp_ctx)
582-
}
583-
584-
#[cfg(test)]
585-
fn as_tlv_stream(&self) -> FullInvoiceRequestTlvStreamRef {
586-
let (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) =
587-
self.contents.as_tlv_stream();
588-
let signature_tlv_stream = SignatureTlvStreamRef {
589-
signature: Some(&self.signature),
590-
};
591-
(payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, signature_tlv_stream)
636+
InvoiceBuilder::for_offer_using_keys(
637+
&self.inner, payment_paths, created_at, payment_hash, keys
638+
)
592639
}
593640
}
594641

0 commit comments

Comments
 (0)