///
/// Also, sets the metadata when [`OfferBuilder::build`] is called such that it can be used by
/// [`InvoiceRequest::verify`] to determine if the request was produced for the offer given an
- /// [`ExpandedKey`].
+ /// [`ExpandedKey`]. However, if [`OfferBuilder::path`] is called, then the metadata will not be
+ /// set and must be included in each [`BlindedPath`] instead. In this case, use
+ /// [`InvoiceRequest::verify_using_recipient_data`].
///
/// [`InvoiceRequest::verify`]: crate::offers::invoice_request::InvoiceRequest::verify
+ /// [`InvoiceRequest::verify_using_recipient_data`]: crate::offers::invoice_request::InvoiceRequest::verify_using_recipient_data
/// [`ExpandedKey`]: crate::ln::inbound_payment::ExpandedKey
pub fn deriving_signing_pubkey(
node_id: PublicKey, expanded_key: &ExpandedKey, nonce: Nonce,
}
fn build_without_checks($($self_mut)* $self: $self_type) -> Offer {
- // Create the metadata for stateless verification of an InvoiceRequest.
if let Some(mut metadata) = $self.offer.metadata.take() {
+ // Create the metadata for stateless verification of an InvoiceRequest.
if metadata.has_derivation_material() {
+
+ // Don't derive keys if no blinded paths were given since this means the signing
+ // pubkey must be the node id of an announced node.
if $self.offer.paths.is_none() {
metadata = metadata.without_keys();
}
tlv_stream.node_id = None;
}
+ // Either replace the signing pubkey with the derived pubkey or include the metadata
+ // for verification. In the former case, the blinded paths must include
+ // `OffersContext::InvoiceRequest` instead.
let (derived_metadata, keys) = metadata.derive_from(tlv_stream, $self.secp_ctx);
- metadata = derived_metadata;
- if let Some(keys) = keys {
- $self.offer.signing_pubkey = Some(keys.public_key());
+ match keys {
+ Some(keys) => $self.offer.signing_pubkey = Some(keys.public_key()),
+ None => $self.offer.metadata = Some(derived_metadata),
}
+ } else {
+ $self.offer.metadata = Some(metadata);
}
-
- $self.offer.metadata = Some(metadata);
}
let mut bytes = Vec::new();
#[cfg(async_payments)]
pub(super) fn verify<T: secp256k1::Signing>(
- &self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
+ &self, nonce: Nonce, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
) -> Result<(OfferId, Option<Keypair>), ()> {
- self.contents.verify(&self.bytes, key, secp_ctx)
+ self.contents.verify_using_recipient_data(&self.bytes, nonce, key, secp_ctx)
}
}
let offer = OfferBuilder::deriving_signing_pubkey(node_id, &expanded_key, nonce, &secp_ctx)
.amount_msats(1000)
.build().unwrap();
+ assert!(offer.metadata().is_some());
assert_eq!(offer.signing_pubkey(), Some(node_id));
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.amount_msats(1000)
.path(blinded_path)
.build().unwrap();
+ assert!(offer.metadata().is_none());
assert_ne!(offer.signing_pubkey(), Some(node_id));
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap()
.sign(payer_sign).unwrap();
- match invoice_request.verify(&expanded_key, &secp_ctx) {
+ match invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx) {
Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
Err(_) => panic!("unexpected error"),
}
+ // Fails verification when using the wrong method
let invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap()
.sign(payer_sign).unwrap();
- match invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx) {
- Ok(invoice_request) => assert_eq!(invoice_request.offer_id, offer.id()),
- Err(_) => panic!("unexpected error"),
- }
+ assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_err());
// Fails verification with altered offer field
let mut tlv_stream = offer.as_tlv_stream();
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap()
.sign(payer_sign).unwrap();
- assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_err());
+ assert!(
+ invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx).is_err()
+ );
// Fails verification with altered signing pubkey
let mut tlv_stream = offer.as_tlv_stream();
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.build().unwrap()
.sign(payer_sign).unwrap();
- assert!(invoice_request.verify(&expanded_key, &secp_ctx).is_err());
+ assert!(
+ invoice_request.verify_using_recipient_data(nonce, &expanded_key, &secp_ctx).is_err()
+ );
}
#[test]
use crate::offers::merkle::{
self, SignError, SignFn, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash,
};
+use crate::offers::nonce::Nonce;
use crate::offers::offer::{
Amount, Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef, Quantity,
};
pub fn for_offer_using_derived_keys<T: secp256k1::Signing>(
offer: &'a Offer, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
message_paths: Vec<BlindedPath>, created_at: Duration, expanded_key: &ExpandedKey,
- secp_ctx: &Secp256k1<T>,
+ nonce: Nonce, secp_ctx: &Secp256k1<T>,
) -> Result<Self, Bolt12SemanticError> {
if offer.chains().len() > 1 {
return Err(Bolt12SemanticError::UnexpectedChain);
offer.signing_pubkey().ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
let keys = offer
- .verify(&expanded_key, &secp_ctx)
+ .verify(nonce, &expanded_key, &secp_ctx)
.map_err(|()| Bolt12SemanticError::InvalidMetadata)?
.1
.ok_or(Bolt12SemanticError::MissingSigningPubkey)?;
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
)
.unwrap()
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
)
.unwrap()
invoice.write(&mut buffer).unwrap();
assert_eq!(invoice.bytes, buffer.as_slice());
- assert!(invoice.metadata().is_some());
+ assert_eq!(invoice.metadata(), None);
assert_eq!(invoice.amount(), None);
assert_eq!(invoice.description(), None);
assert_eq!(invoice.offer_features(), &OfferFeatures::empty());
);
let paths = vec![blinded_path()];
- let metadata = vec![42; 16];
assert_eq!(
invoice.as_tlv_stream(),
(
OfferTlvStreamRef {
chains: None,
- metadata: Some(&metadata),
+ metadata: None,
currency: None,
amount: None,
description: None,
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
)
.unwrap()
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
)
.unwrap()
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
) {
assert_eq!(e, Bolt12SemanticError::MissingPaths);
Vec::new(),
now,
&expanded_key,
+ nonce,
&secp_ctx,
) {
assert_eq!(e, Bolt12SemanticError::MissingPaths);
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
) {
assert_eq!(e, Bolt12SemanticError::MissingPaths);
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
) {
assert_eq!(e, Bolt12SemanticError::MissingSigningPubkey);
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
) {
assert_eq!(e, Bolt12SemanticError::InvalidMetadata);
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
) {
assert_eq!(e, Bolt12SemanticError::UnexpectedChain);
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
)
.unwrap()
vec![blinded_path()],
now,
&expanded_key,
+ nonce,
&secp_ctx,
)
.unwrap()