Skip to content

Commit b70570b

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 3b76562 commit b70570b

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
@@ -1252,36 +1252,31 @@ mod tests {
12521252
.build().unwrap()
12531253
.sign(payer_sign).unwrap();
12541254

1255-
if let Err(e) = invoice_request
1256-
.verify_and_respond_using_derived_keys_no_std(
1257-
payment_paths(), payment_hash(), now(), &expanded_key, &secp_ctx
1258-
)
1259-
.unwrap()
1255+
if let Err(e) = invoice_request.clone()
1256+
.verify(&expanded_key, &secp_ctx).unwrap()
1257+
.respond_using_derived_keys_no_std(payment_paths(), payment_hash(), now()).unwrap()
12601258
.build_and_sign(&secp_ctx)
12611259
{
12621260
panic!("error building invoice: {:?}", e);
12631261
}
12641262

12651263
let expanded_key = ExpandedKey::new(&KeyMaterial([41; 32]));
1266-
match invoice_request.verify_and_respond_using_derived_keys_no_std(
1267-
payment_paths(), payment_hash(), now(), &expanded_key, &secp_ctx
1268-
) {
1269-
Ok(_) => panic!("expected error"),
1270-
Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidMetadata),
1271-
}
1264+
assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_err());
12721265

12731266
let desc = "foo".to_string();
12741267
let offer = OfferBuilder
12751268
::deriving_signing_pubkey(desc, node_id, &expanded_key, &entropy, &secp_ctx)
12761269
.amount_msats(1000)
1270+
// Omit the path so that node_id is used for the signing pubkey instead of deriving
12771271
.build().unwrap();
12781272
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
12791273
.build().unwrap()
12801274
.sign(payer_sign).unwrap();
12811275

1282-
match invoice_request.verify_and_respond_using_derived_keys_no_std(
1283-
payment_paths(), payment_hash(), now(), &expanded_key, &secp_ctx
1284-
) {
1276+
match invoice_request
1277+
.verify(&expanded_key, &secp_ctx).unwrap()
1278+
.respond_using_derived_keys_no_std(payment_paths(), payment_hash(), now())
1279+
{
12851280
Ok(_) => panic!("expected error"),
12861281
Err(e) => assert_eq!(e, Bolt12SemanticError::InvalidMetadata),
12871282
}

lightning/src/offers/invoice_request.rs

Lines changed: 82 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,22 @@ pub struct InvoiceRequest {
401401
signature: Signature,
402402
}
403403

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

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

545-
self.verify_and_respond_using_derived_keys_no_std(
546-
payment_paths, payment_hash, created_at, expanded_key, secp_ctx
547-
)
614+
self.respond_using_derived_keys_no_std(payment_paths, payment_hash, created_at)
548615
}
549616

550617
/// Creates an [`InvoiceBuilder`] for the request using the given required fields and that uses
@@ -556,42 +623,22 @@ impl InvoiceRequest {
556623
/// This is not exported to bindings users as builder patterns don't map outside of move semantics.
557624
///
558625
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
559-
pub fn verify_and_respond_using_derived_keys_no_std<T: secp256k1::Signing>(
626+
pub fn respond_using_derived_keys_no_std(
560627
&self, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>, payment_hash: PaymentHash,
561-
created_at: core::time::Duration, expanded_key: &ExpandedKey, secp_ctx: &Secp256k1<T>
628+
created_at: core::time::Duration
562629
) -> Result<InvoiceBuilder<DerivedSigningPubkey>, Bolt12SemanticError> {
563-
if self.features().requires_unknown_bits() {
630+
if self.inner.features().requires_unknown_bits() {
564631
return Err(Bolt12SemanticError::UnknownRequiredFeatures);
565632
}
566633

567-
let keys = match self.verify(expanded_key, secp_ctx) {
568-
Err(()) => return Err(Bolt12SemanticError::InvalidMetadata),
569-
Ok(None) => return Err(Bolt12SemanticError::InvalidMetadata),
570-
Ok(Some(keys)) => keys,
634+
let keys = match self.keys {
635+
None => return Err(Bolt12SemanticError::InvalidMetadata),
636+
Some(keys) => keys,
571637
};
572638

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

0 commit comments

Comments
 (0)