Skip to content

Commit 96e7d7a

Browse files
authored
Merge pull request #2687 from orbitalturtle/signature-data-enum
Expose more granular data in TaggedHash struct
2 parents 2e33acb + caafced commit 96e7d7a

File tree

3 files changed

+57
-20
lines changed

3 files changed

+57
-20
lines changed

lightning/src/offers/invoice.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ impl UnsignedBolt12Invoice {
439439
bytes: self.bytes,
440440
contents: self.contents,
441441
signature,
442+
tagged_hash: self.tagged_hash,
442443
})
443444
}
444445
}
@@ -463,6 +464,7 @@ pub struct Bolt12Invoice {
463464
bytes: Vec<u8>,
464465
contents: InvoiceContents,
465466
signature: Signature,
467+
tagged_hash: TaggedHash,
466468
}
467469

468470
/// The contents of an [`Bolt12Invoice`] for responding to either an [`Offer`] or a [`Refund`].
@@ -707,7 +709,7 @@ impl Bolt12Invoice {
707709

708710
/// Hash that was used for signing the invoice.
709711
pub fn signable_hash(&self) -> [u8; 32] {
710-
merkle::message_digest(SIGNATURE_TAG, &self.bytes).as_ref().clone()
712+
self.tagged_hash.as_digest().as_ref().clone()
711713
}
712714

713715
/// Verifies that the invoice was for a request or refund created using the given key. Returns
@@ -1212,11 +1214,11 @@ impl TryFrom<ParsedMessage<FullInvoiceTlvStream>> for Bolt12Invoice {
12121214
None => return Err(Bolt12ParseError::InvalidSemantics(Bolt12SemanticError::MissingSignature)),
12131215
Some(signature) => signature,
12141216
};
1215-
let message = TaggedHash::new(SIGNATURE_TAG, &bytes);
1217+
let tagged_hash = TaggedHash::new(SIGNATURE_TAG, &bytes);
12161218
let pubkey = contents.fields().signing_pubkey;
1217-
merkle::verify_signature(&signature, message, pubkey)?;
1219+
merkle::verify_signature(&signature, &tagged_hash, pubkey)?;
12181220

1219-
Ok(Bolt12Invoice { bytes, contents, signature })
1221+
Ok(Bolt12Invoice { bytes, contents, signature, tagged_hash })
12201222
}
12211223
}
12221224

@@ -1431,7 +1433,7 @@ mod tests {
14311433
assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
14321434

14331435
let message = TaggedHash::new(SIGNATURE_TAG, &invoice.bytes);
1434-
assert!(merkle::verify_signature(&invoice.signature, message, recipient_pubkey()).is_ok());
1436+
assert!(merkle::verify_signature(&invoice.signature, &message, recipient_pubkey()).is_ok());
14351437

14361438
let digest = Message::from_slice(&invoice.signable_hash()).unwrap();
14371439
let pubkey = recipient_pubkey().into();
@@ -1528,7 +1530,7 @@ mod tests {
15281530
assert_eq!(invoice.signing_pubkey(), recipient_pubkey());
15291531

15301532
let message = TaggedHash::new(SIGNATURE_TAG, &invoice.bytes);
1531-
assert!(merkle::verify_signature(&invoice.signature, message, recipient_pubkey()).is_ok());
1533+
assert!(merkle::verify_signature(&invoice.signature, &message, recipient_pubkey()).is_ok());
15321534

15331535
assert_eq!(
15341536
invoice.as_tlv_stream(),

lightning/src/offers/invoice_request.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,7 @@ impl TryFrom<Vec<u8>> for InvoiceRequest {
876876
Some(signature) => signature,
877877
};
878878
let message = TaggedHash::new(SIGNATURE_TAG, &bytes);
879-
merkle::verify_signature(&signature, message, contents.payer_id)?;
879+
merkle::verify_signature(&signature, &message, contents.payer_id)?;
880880

881881
Ok(InvoiceRequest { bytes, contents, signature })
882882
}
@@ -1013,7 +1013,7 @@ mod tests {
10131013
assert_eq!(invoice_request.payer_note(), None);
10141014

10151015
let message = TaggedHash::new(SIGNATURE_TAG, &invoice_request.bytes);
1016-
assert!(merkle::verify_signature(&invoice_request.signature, message, payer_pubkey()).is_ok());
1016+
assert!(merkle::verify_signature(&invoice_request.signature, &message, payer_pubkey()).is_ok());
10171017

10181018
assert_eq!(
10191019
invoice_request.as_tlv_stream(),

lightning/src/offers/merkle.rs

+47-12
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,41 @@ tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, SIGNATURE_TYPES, {
3030
///
3131
/// [BIP 340]: https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
3232
/// [BOLT 12]: https://github.com/rustyrussell/lightning-rfc/blob/guilt/offers/12-offer-encoding.md#signature-calculation
33-
#[derive(Debug, PartialEq)]
34-
pub struct TaggedHash(Message);
33+
#[derive(Clone, Debug, PartialEq)]
34+
pub struct TaggedHash {
35+
tag: String,
36+
merkle_root: sha256::Hash,
37+
digest: Message,
38+
}
3539

3640
impl TaggedHash {
3741
/// Creates a tagged hash with the given parameters.
3842
///
3943
/// Panics if `tlv_stream` is not a well-formed TLV stream containing at least one TLV record.
4044
pub(super) fn new(tag: &str, tlv_stream: &[u8]) -> Self {
41-
Self(message_digest(tag, tlv_stream))
45+
let tag_hash = sha256::Hash::hash(tag.as_bytes());
46+
let merkle_root = root_hash(tlv_stream);
47+
let digest = Message::from_slice(&tagged_hash(tag_hash, merkle_root)).unwrap();
48+
Self {
49+
tag: tag.to_owned(),
50+
merkle_root,
51+
digest,
52+
}
4253
}
4354

4455
/// Returns the digest to sign.
4556
pub fn as_digest(&self) -> &Message {
46-
&self.0
57+
&self.digest
58+
}
59+
60+
/// Returns the tag used in the tagged hash.
61+
pub fn tag(&self) -> &str {
62+
&self.tag
63+
}
64+
65+
/// Returns the merkle root used in the tagged hash.
66+
pub fn merkle_root(&self) -> sha256::Hash {
67+
self.merkle_root
4768
}
4869
}
4970

@@ -91,20 +112,14 @@ where
91112
/// Verifies the signature with a pubkey over the given message using a tagged hash as the message
92113
/// digest.
93114
pub(super) fn verify_signature(
94-
signature: &Signature, message: TaggedHash, pubkey: PublicKey,
115+
signature: &Signature, message: &TaggedHash, pubkey: PublicKey,
95116
) -> Result<(), secp256k1::Error> {
96117
let digest = message.as_digest();
97118
let pubkey = pubkey.into();
98119
let secp_ctx = Secp256k1::verification_only();
99120
secp_ctx.verify_schnorr(signature, digest, &pubkey)
100121
}
101122

102-
pub(super) fn message_digest(tag: &str, bytes: &[u8]) -> Message {
103-
let tag = sha256::Hash::hash(tag.as_bytes());
104-
let merkle_root = root_hash(bytes);
105-
Message::from_slice(&tagged_hash(tag, merkle_root)).unwrap()
106-
}
107-
108123
/// Computes a merkle root hash for the given data, which must be a well-formed TLV stream
109124
/// containing at least one TLV record.
110125
fn root_hash(data: &[u8]) -> sha256::Hash {
@@ -258,12 +273,13 @@ mod tests {
258273
use super::{SIGNATURE_TYPES, TlvStream, WithoutSignatures};
259274

260275
use bitcoin::hashes::{Hash, sha256};
261-
use bitcoin::secp256k1::{KeyPair, Secp256k1, SecretKey};
276+
use bitcoin::secp256k1::{KeyPair, Message, Secp256k1, SecretKey};
262277
use bitcoin::secp256k1::schnorr::Signature;
263278
use core::convert::Infallible;
264279
use crate::offers::offer::{Amount, OfferBuilder};
265280
use crate::offers::invoice_request::InvoiceRequest;
266281
use crate::offers::parse::Bech32Encode;
282+
use crate::offers::test_utils::{payer_pubkey, recipient_pubkey};
267283
use crate::util::ser::Writeable;
268284

269285
#[test]
@@ -322,6 +338,25 @@ mod tests {
322338
);
323339
}
324340

341+
#[test]
342+
fn compute_tagged_hash() {
343+
let unsigned_invoice_request = OfferBuilder::new("foo".into(), recipient_pubkey())
344+
.amount_msats(1000)
345+
.build().unwrap()
346+
.request_invoice(vec![1; 32], payer_pubkey()).unwrap()
347+
.payer_note("bar".into())
348+
.build().unwrap();
349+
350+
// Simply test that we can grab the tag and merkle root exposed by the accessor
351+
// functions, then use them to succesfully compute a tagged hash.
352+
let tagged_hash = unsigned_invoice_request.as_ref();
353+
let expected_digest = unsigned_invoice_request.as_ref().as_digest();
354+
let tag = sha256::Hash::hash(tagged_hash.tag().as_bytes());
355+
let actual_digest = Message::from_slice(&super::tagged_hash(tag, tagged_hash.merkle_root()))
356+
.unwrap();
357+
assert_eq!(*expected_digest, actual_digest);
358+
}
359+
325360
#[test]
326361
fn skips_encoding_signature_tlv_records() {
327362
let secp_ctx = Secp256k1::new();

0 commit comments

Comments
 (0)