X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Foffers%2Finvoice_request.rs;h=b05f9af8481bff6ed333856cc74e7c28e5f43d59;hb=270bc2e4c07f2891c8fbbc2339297565bf529050;hp=f014bf120021b52b613a6726f988e8fb6b076f1b;hpb=baf9731a21f695baf1e5fe562b9484f5f56f99e6;p=rust-lightning diff --git a/lightning/src/offers/invoice_request.rs b/lightning/src/offers/invoice_request.rs index f014bf12..b05f9af8 100644 --- a/lightning/src/offers/invoice_request.rs +++ b/lightning/src/offers/invoice_request.rs @@ -44,7 +44,9 @@ //! .quantity(5)? //! .payer_note("foo".to_string()) //! .build()? -//! .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))) +//! .sign::<_, Infallible>( +//! |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys)) +//! ) //! .expect("failed verifying signature") //! .write(&mut buffer) //! .unwrap(); @@ -54,9 +56,9 @@ use bitcoin::blockdata::constants::ChainHash; use bitcoin::network::constants::Network; -use bitcoin::secp256k1::{KeyPair, Message, PublicKey, Secp256k1, self}; +use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, self}; use bitcoin::secp256k1::schnorr::Signature; -use core::convert::{Infallible, TryFrom}; +use core::convert::{AsRef, Infallible, TryFrom}; use core::ops::Deref; use crate::sign::EntropySource; use crate::io; @@ -66,7 +68,7 @@ use crate::ln::features::InvoiceRequestFeatures; use crate::ln::inbound_payment::{ExpandedKey, IV_LEN, Nonce}; use crate::ln::msgs::DecodeError; use crate::offers::invoice::{BlindedPayInfo, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder}; -use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, self}; +use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, self}; use crate::offers::offer::{Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef}; use crate::offers::parse::{Bolt12ParseError, ParsedMessage, Bolt12SemanticError}; use crate::offers::payer::{PayerContents, PayerTlvStream, PayerTlvStreamRef}; @@ -76,7 +78,8 @@ use crate::util::string::PrintableString; use crate::prelude::*; -const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice_request", "signature"); +/// Tag for the hash function used when signing an [`InvoiceRequest`]'s merkle root. +pub const SIGNATURE_TAG: &'static str = concat!("lightning", "invoice_request", "signature"); pub(super) const IV_BYTES: &[u8; IV_LEN] = b"LDK Invreq ~~~~~"; @@ -214,7 +217,7 @@ impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a } fn build_with_checks(mut self) -> Result< - (UnsignedInvoiceRequest<'a>, Option, Option<&'b Secp256k1>), + (UnsignedInvoiceRequest, Option, Option<&'b Secp256k1>), Bolt12SemanticError > { #[cfg(feature = "std")] { @@ -245,7 +248,7 @@ impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a } fn build_without_checks(mut self) -> - (UnsignedInvoiceRequest<'a>, Option, Option<&'b Secp256k1>) + (UnsignedInvoiceRequest, Option, Option<&'b Secp256k1>) { // Create the metadata for stateless verification of a Bolt12Invoice. let mut keys = None; @@ -275,22 +278,20 @@ impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a debug_assert!(self.payer_id.is_some()); let payer_id = self.payer_id.unwrap(); - let unsigned_invoice = UnsignedInvoiceRequest { - offer: self.offer, - invoice_request: InvoiceRequestContents { - inner: self.invoice_request, - payer_id, - }, + let invoice_request = InvoiceRequestContents { + inner: self.invoice_request, + payer_id, }; + let unsigned_invoice_request = UnsignedInvoiceRequest::new(self.offer, invoice_request); - (unsigned_invoice, keys, secp_ctx) + (unsigned_invoice_request, keys, secp_ctx) } } impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, ExplicitPayerId, T> { /// Builds an unsigned [`InvoiceRequest`] after checking for valid semantics. It can be signed /// by [`UnsignedInvoiceRequest::sign`]. - pub fn build(self) -> Result, Bolt12SemanticError> { + pub fn build(self) -> Result { let (unsigned_invoice_request, keys, _) = self.build_with_checks()?; debug_assert!(keys.is_none()); Ok(unsigned_invoice_request) @@ -306,7 +307,9 @@ impl<'a, 'b, T: secp256k1::Signing> InvoiceRequestBuilder<'a, 'b, DerivedPayerId let secp_ctx = secp_ctx.unwrap(); let keys = keys.unwrap(); let invoice_request = unsigned_invoice_request - .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))) + .sign::<_, Infallible>( + |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys)) + ) .unwrap(); Ok(invoice_request) } @@ -335,52 +338,72 @@ impl<'a, 'b, P: PayerIdStrategy, T: secp256k1::Signing> InvoiceRequestBuilder<'a self } - pub(super) fn build_unchecked(self) -> UnsignedInvoiceRequest<'a> { + pub(super) fn build_unchecked(self) -> UnsignedInvoiceRequest { self.build_without_checks().0 } } /// A semantically valid [`InvoiceRequest`] that hasn't been signed. -pub struct UnsignedInvoiceRequest<'a> { - offer: &'a Offer, - invoice_request: InvoiceRequestContents, +/// +/// # Serialization +/// +/// This is serialized as a TLV stream, which includes TLV records from the originating message. As +/// such, it may include unknown, odd TLV records. +pub struct UnsignedInvoiceRequest { + bytes: Vec, + contents: InvoiceRequestContents, + tagged_hash: TaggedHash, } -impl<'a> UnsignedInvoiceRequest<'a> { - /// Signs the invoice request using the given function. - /// - /// This is not exported to bindings users as functions are not yet mapped. - pub fn sign(self, sign: F) -> Result> - where - F: FnOnce(&Message) -> Result - { +impl UnsignedInvoiceRequest { + fn new(offer: &Offer, contents: InvoiceRequestContents) -> Self { // Use the offer bytes instead of the offer TLV stream as the offer may have contained // unknown TLV records, which are not stored in `OfferContents`. let (payer_tlv_stream, _offer_tlv_stream, invoice_request_tlv_stream) = - self.invoice_request.as_tlv_stream(); - let offer_bytes = WithoutLength(&self.offer.bytes); + contents.as_tlv_stream(); + let offer_bytes = WithoutLength(&offer.bytes); let unsigned_tlv_stream = (payer_tlv_stream, offer_bytes, invoice_request_tlv_stream); let mut bytes = Vec::new(); unsigned_tlv_stream.write(&mut bytes).unwrap(); - let pubkey = self.invoice_request.payer_id; - let signature = merkle::sign_message(sign, SIGNATURE_TAG, &bytes, pubkey)?; + let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes); + + Self { bytes, contents, tagged_hash } + } + + /// Signs the [`TaggedHash`] of the invoice request using the given function. + /// + /// Note: The hash computation may have included unknown, odd TLV records. + /// + /// This is not exported to bindings users as functions are not yet mapped. + pub fn sign(mut self, sign: F) -> Result> + where + F: FnOnce(&Self) -> Result + { + let pubkey = self.contents.payer_id; + let signature = merkle::sign_message(sign, &self, pubkey)?; // Append the signature TLV record to the bytes. let signature_tlv_stream = SignatureTlvStreamRef { signature: Some(&signature), }; - signature_tlv_stream.write(&mut bytes).unwrap(); + signature_tlv_stream.write(&mut self.bytes).unwrap(); Ok(InvoiceRequest { - bytes, - contents: self.invoice_request, + bytes: self.bytes, + contents: self.contents, signature, }) } } +impl AsRef for UnsignedInvoiceRequest { + fn as_ref(&self) -> &TaggedHash { + &self.tagged_hash + } +} + /// An `InvoiceRequest` is a request for a [`Bolt12Invoice`] formulated from an [`Offer`]. /// /// An offer may provide choices such as quantity, amount, chain, features, etc. An invoice request @@ -591,7 +614,7 @@ impl InvoiceRequest { } impl InvoiceRequestContents { - pub fn metadata(&self) -> &[u8] { + pub(super) fn metadata(&self) -> &[u8] { self.inner.metadata() } @@ -648,6 +671,12 @@ impl InvoiceRequestContentsWithoutPayerId { } } +impl Writeable for UnsignedInvoiceRequest { + fn write(&self, writer: &mut W) -> Result<(), io::Error> { + WithoutLength(&self.bytes).write(writer) + } +} + impl Writeable for InvoiceRequest { fn write(&self, writer: &mut W) -> Result<(), io::Error> { WithoutLength(&self.bytes).write(writer) @@ -707,6 +736,25 @@ type PartialInvoiceRequestTlvStreamRef<'a> = ( InvoiceRequestTlvStreamRef<'a>, ); +impl TryFrom> for UnsignedInvoiceRequest { + type Error = Bolt12ParseError; + + fn try_from(bytes: Vec) -> Result { + let invoice_request = ParsedMessage::::try_from(bytes)?; + let ParsedMessage { bytes, tlv_stream } = invoice_request; + let ( + payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream, + ) = tlv_stream; + let contents = InvoiceRequestContents::try_from( + (payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream) + )?; + + let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes); + + Ok(UnsignedInvoiceRequest { bytes, contents, tagged_hash }) + } +} + impl TryFrom> for InvoiceRequest { type Error = Bolt12ParseError; @@ -776,7 +824,7 @@ impl TryFrom for InvoiceRequestContents { #[cfg(test)] mod tests { - use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG}; + use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG, UnsignedInvoiceRequest}; use bitcoin::blockdata::constants::ChainHash; use bitcoin::network::constants::Network; @@ -790,7 +838,7 @@ mod tests { use crate::ln::inbound_payment::ExpandedKey; use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT}; use crate::offers::invoice::{Bolt12Invoice, SIGNATURE_TAG as INVOICE_SIGNATURE_TAG}; - use crate::offers::merkle::{SignError, SignatureTlvStreamRef, self}; + use crate::offers::merkle::{SignError, SignatureTlvStreamRef, TaggedHash, self}; use crate::offers::offer::{Amount, OfferBuilder, OfferTlvStreamRef, Quantity}; use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError}; use crate::offers::payer::PayerTlvStreamRef; @@ -800,12 +848,24 @@ mod tests { #[test] fn builds_invoice_request_with_defaults() { - let invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey()) + let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) .build().unwrap() .request_invoice(vec![1; 32], payer_pubkey()).unwrap() - .build().unwrap() - .sign(payer_sign).unwrap(); + .build().unwrap(); + + let mut buffer = Vec::new(); + unsigned_invoice_request.write(&mut buffer).unwrap(); + + match UnsignedInvoiceRequest::try_from(buffer) { + Err(e) => panic!("error parsing unsigned invoice request: {:?}", e), + Ok(parsed) => { + assert_eq!(parsed.bytes, unsigned_invoice_request.bytes); + assert_eq!(parsed.tagged_hash, unsigned_invoice_request.tagged_hash); + }, + } + + let invoice_request = unsigned_invoice_request.sign(payer_sign).unwrap(); let mut buffer = Vec::new(); invoice_request.write(&mut buffer).unwrap(); @@ -922,9 +982,8 @@ mod tests { let mut bytes = Vec::new(); tlv_stream.write(&mut bytes).unwrap(); - let signature = merkle::sign_message( - recipient_sign, INVOICE_SIGNATURE_TAG, &bytes, recipient_pubkey() - ).unwrap(); + let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes); + let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap(); signature_tlv_stream.signature = Some(&signature); let mut encoded_invoice = bytes; @@ -946,9 +1005,8 @@ mod tests { let mut bytes = Vec::new(); tlv_stream.write(&mut bytes).unwrap(); - let signature = merkle::sign_message( - recipient_sign, INVOICE_SIGNATURE_TAG, &bytes, recipient_pubkey() - ).unwrap(); + let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes); + let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap(); signature_tlv_stream.signature = Some(&signature); let mut encoded_invoice = bytes; @@ -992,9 +1050,8 @@ mod tests { let mut bytes = Vec::new(); tlv_stream.write(&mut bytes).unwrap(); - let signature = merkle::sign_message( - recipient_sign, INVOICE_SIGNATURE_TAG, &bytes, recipient_pubkey() - ).unwrap(); + let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes); + let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap(); signature_tlv_stream.signature = Some(&signature); let mut encoded_invoice = bytes; @@ -1016,9 +1073,8 @@ mod tests { let mut bytes = Vec::new(); tlv_stream.write(&mut bytes).unwrap(); - let signature = merkle::sign_message( - recipient_sign, INVOICE_SIGNATURE_TAG, &bytes, recipient_pubkey() - ).unwrap(); + let message = TaggedHash::new(INVOICE_SIGNATURE_TAG, &bytes); + let signature = merkle::sign_message(recipient_sign, &message, recipient_pubkey()).unwrap(); signature_tlv_stream.signature = Some(&signature); let mut encoded_invoice = bytes; @@ -1669,7 +1725,7 @@ mod tests { .build().unwrap(); let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap(); - let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream(); + let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream(); tlv_stream.0.metadata = None; let mut buffer = Vec::new(); @@ -1690,7 +1746,7 @@ mod tests { .build().unwrap(); let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap(); - let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream(); + let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream(); tlv_stream.2.payer_id = None; let mut buffer = Vec::new(); @@ -1709,7 +1765,7 @@ mod tests { .build().unwrap(); let unsigned_invoice_request = offer.request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap(); - let mut tlv_stream = unsigned_invoice_request.invoice_request.as_tlv_stream(); + let mut tlv_stream = unsigned_invoice_request.contents.as_tlv_stream(); tlv_stream.1.node_id = None; let mut buffer = Vec::new(); @@ -1731,7 +1787,7 @@ mod tests { .build().unwrap() .request_invoice(vec![1; 32], payer_pubkey()).unwrap() .build().unwrap() - .invoice_request + .contents .write(&mut buffer).unwrap(); match InvoiceRequest::try_from(buffer) { @@ -1771,7 +1827,9 @@ mod tests { .build().unwrap() .request_invoice(vec![1; 32], keys.public_key()).unwrap() .build().unwrap() - .sign::<_, Infallible>(|digest| Ok(secp_ctx.sign_schnorr_no_aux_rand(digest, &keys))) + .sign::<_, Infallible>( + |message| Ok(secp_ctx.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys)) + ) .unwrap(); let mut encoded_invoice_request = Vec::new();