Skip to content

Commit

Permalink
expose more granular data in TaggedHash struct
Browse files Browse the repository at this point in the history
  • Loading branch information
orbitalturtle committed Oct 30, 2023
1 parent d2242f6 commit 74b35eb
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 11 deletions.
5 changes: 3 additions & 2 deletions lightning/src/offers/invoice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, InvoiceRequ
use crate::ln::inbound_payment::ExpandedKey;
use crate::ln::msgs::DecodeError;
use crate::offers::invoice_request::{INVOICE_REQUEST_PAYER_ID_TYPE, INVOICE_REQUEST_TYPES, IV_BYTES as INVOICE_REQUEST_IV_BYTES, InvoiceRequest, InvoiceRequestContents, InvoiceRequestTlvStream, InvoiceRequestTlvStreamRef};
use crate::offers::merkle::{SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, WithoutSignatures, self};
use crate::offers::merkle::{root_hash, SignError, SignatureTlvStream, SignatureTlvStreamRef, TaggedHash, TlvStream, WithoutSignatures, self};
use crate::offers::offer::{Amount, OFFER_TYPES, OfferTlvStream, OfferTlvStreamRef, Quantity};
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef};
Expand Down Expand Up @@ -707,7 +707,8 @@ impl Bolt12Invoice {

/// Hash that was used for signing the invoice.
pub fn signable_hash(&self) -> [u8; 32] {
merkle::message_digest(SIGNATURE_TAG, &self.bytes).as_ref().clone()
let merkle_root_hash = root_hash(&self.bytes);
merkle::message_digest(SIGNATURE_TAG, merkle_root_hash).as_ref().clone()
}

/// Verifies that the invoice was for a request or refund created using the given key. Returns
Expand Down
22 changes: 20 additions & 2 deletions lightning/src/offers/invoice_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -930,8 +930,9 @@ mod tests {
use super::{InvoiceRequest, InvoiceRequestTlvStreamRef, SIGNATURE_TAG, UnsignedInvoiceRequest};

use bitcoin::blockdata::constants::ChainHash;
use bitcoin::hashes::{sha256, Hash};
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey, self};
use bitcoin::secp256k1::{KeyPair, Message, Secp256k1, SecretKey, self};
use core::convert::{Infallible, TryFrom};
use core::num::NonZeroU64;
#[cfg(feature = "std")]
Expand All @@ -942,7 +943,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, TaggedHash, self};
use crate::offers::merkle::{tagged_hash, SignError, SignatureTlvStreamRef, TaggedHash, self};
use crate::offers::offer::{Amount, OfferBuilder, OfferTlvStreamRef, Quantity};
use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError};
use crate::offers::payer::PayerTlvStreamRef;
Expand Down Expand Up @@ -1545,6 +1546,23 @@ mod tests {
assert_eq!(tlv_stream.payer_note, Some(&String::from("baz")));
}

#[test]
fn compute_tagged_hash() {
let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
.payer_note("bar".into())
.build().unwrap();

// Simply test that we can grab the tag and merkle root exposed by the accessor
// functions, then use them tosuccesfully compute a tagged hash.
let taggedhash = unsigned_invoice_request.as_ref();
let tag = sha256::Hash::hash(taggedhash.tag().as_bytes());
let _ = Message::from_slice(&tagged_hash(tag, taggedhash.merkle_root()))
.unwrap();
}

#[test]
fn fails_signing_invoice_request() {
match OfferBuilder::new("foo".into(), recipient_pubkey())
Expand Down
32 changes: 25 additions & 7 deletions lightning/src/offers/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,38 @@ tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, SIGNATURE_TYPES, {
/// [BIP 340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
/// [BOLT 12]: https://github.com/rustyrussell/lightning-rfc/blob/guilt/offers/12-offer-encoding.md#signature-calculation
#[derive(Debug, PartialEq)]
pub struct TaggedHash(Message);
pub struct TaggedHash {
tag: String,
merkle_root: sha256::Hash,
digest: Message,
}

impl TaggedHash {
/// Creates a tagged hash with the given parameters.
///
/// Panics if `tlv_stream` is not a well-formed TLV stream containing at least one TLV record.
pub(super) fn new(tag: &str, tlv_stream: &[u8]) -> Self {
Self(message_digest(tag, tlv_stream))
let merkle_root = root_hash(tlv_stream);
Self {
tag: tag.to_owned(),
merkle_root,
digest: message_digest(tag, merkle_root),
}
}

/// Returns the digest to sign.
pub fn as_digest(&self) -> &Message {
&self.0
&self.digest
}

/// Returns the tag used in the tagged hash.
pub fn tag(&self) -> &str {
&self.tag
}

/// Returns the merkle root used in the tagged hash.
pub fn merkle_root(&self) -> sha256::Hash {
self.merkle_root
}
}

Expand Down Expand Up @@ -99,15 +118,14 @@ pub(super) fn verify_signature(
secp_ctx.verify_schnorr(signature, digest, &pubkey)
}

pub(super) fn message_digest(tag: &str, bytes: &[u8]) -> Message {
pub(super) fn message_digest(tag: &str, merkle_root: sha256::Hash) -> Message {
let tag = sha256::Hash::hash(tag.as_bytes());
let merkle_root = root_hash(bytes);
Message::from_slice(&tagged_hash(tag, merkle_root)).unwrap()
}

/// Computes a merkle root hash for the given data, which must be a well-formed TLV stream
/// containing at least one TLV record.
fn root_hash(data: &[u8]) -> sha256::Hash {
pub(crate) fn root_hash(data: &[u8]) -> sha256::Hash {
let nonce_tag = tagged_hash_engine(sha256::Hash::from_engine({
let first_tlv_record = TlvStream::new(&data[..]).next().unwrap();
let mut engine = sha256::Hash::engine();
Expand Down Expand Up @@ -144,7 +162,7 @@ fn root_hash(data: &[u8]) -> sha256::Hash {
*leaves.first().unwrap()
}

fn tagged_hash<T: AsRef<[u8]>>(tag: sha256::Hash, msg: T) -> sha256::Hash {
pub(crate) fn tagged_hash<T: AsRef<[u8]>>(tag: sha256::Hash, msg: T) -> sha256::Hash {
let engine = tagged_hash_engine(tag);
tagged_hash_from_engine(engine, msg)
}
Expand Down

0 comments on commit 74b35eb

Please sign in to comment.