+tlv_stream!(SignatureTlvStream, SignatureTlvStreamRef, SIGNATURE_TYPES, {
+ (240, signature: Signature),
+});
+
+/// A hash for use in a specific context by tweaking with a context-dependent tag as per [BIP 340]
+/// and computed over the merkle root of a TLV stream to sign as defined in [BOLT 12].
+///
+/// [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(Clone, Debug, PartialEq)]
+pub struct TaggedHash {
+ tag: &'static str,
+ 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: &'static str, tlv_stream: &[u8]) -> Self {
+ let tag_hash = sha256::Hash::hash(tag.as_bytes());
+ let merkle_root = root_hash(tlv_stream);
+ let digest = Message::from_slice(tagged_hash(tag_hash, merkle_root).as_byte_array()).unwrap();
+ Self {
+ tag,
+ merkle_root,
+ digest,
+ }
+ }
+
+ /// Returns the digest to sign.
+ pub fn as_digest(&self) -> &Message {
+ &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
+ }
+}
+
+impl AsRef<TaggedHash> for TaggedHash {
+ fn as_ref(&self) -> &TaggedHash {
+ self
+ }
+}
+
+/// Error when signing messages.
+#[derive(Debug, PartialEq)]
+pub enum SignError<E> {
+ /// User-defined error when signing the message.
+ Signing(E),
+ /// Error when verifying the produced signature using the given pubkey.
+ Verification(secp256k1::Error),
+}
+
+/// Signs a [`TaggedHash`] computed over the merkle root of `message`'s TLV stream, checking if it
+/// can be verified with the supplied `pubkey`.
+///
+/// Since `message` is any type that implements [`AsRef<TaggedHash>`], `sign` may be a closure that
+/// takes a message such as [`Bolt12Invoice`] or [`InvoiceRequest`]. This allows further message
+/// verification before signing its [`TaggedHash`].
+///
+/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+pub(super) fn sign_message<F, E, T>(
+ sign: F, message: &T, pubkey: PublicKey,
+) -> Result<Signature, SignError<E>>
+where
+ F: FnOnce(&T) -> Result<Signature, E>,
+ T: AsRef<TaggedHash>,
+{
+ let signature = sign(message).map_err(|e| SignError::Signing(e))?;
+
+ let digest = message.as_ref().as_digest();
+ let pubkey = pubkey.into();
+ let secp_ctx = Secp256k1::verification_only();
+ secp_ctx.verify_schnorr(&signature, digest, &pubkey).map_err(|e| SignError::Verification(e))?;
+
+ Ok(signature)
+}
+
+/// Verifies the signature with a pubkey over the given message using a tagged hash as the message
+/// digest.
+pub(super) fn verify_signature(
+ signature: &Signature, message: &TaggedHash, pubkey: PublicKey,
+) -> Result<(), secp256k1::Error> {
+ let digest = message.as_digest();
+ let pubkey = pubkey.into();
+ let secp_ctx = Secp256k1::verification_only();
+ secp_ctx.verify_schnorr(signature, digest, &pubkey)
+}
+