From c740598c5eedfca6dcba82462f915150b99070c3 Mon Sep 17 00:00:00 2001 From: codeShark149 <rajarshi149@gmail.com> Date: Mon, 15 Mar 2021 15:27:55 +0530 Subject: [PATCH 1/2] BIP322 Implementation This PR implements a BIP322 generic message signer with Bitcoin Scripts. --- src/bip322.rs | 337 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 338 insertions(+) create mode 100644 src/bip322.rs diff --git a/src/bip322.rs b/src/bip322.rs new file mode 100644 index 000000000..1ae1b0148 --- /dev/null +++ b/src/bip322.rs @@ -0,0 +1,337 @@ +// BIP322 Generic Signature Algorithm +// Written in 2019 by +// Rajarshi Maitra <rajarshi149@protonmail.com>] +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. +// + +//! # BIP322 Generic Signed Message Structure +//! +//! This module implements the BIP322 Generic Message Signer and Validator +//! +//! `https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki` +//! + +use crate::{Descriptor, DescriptorTrait, MiniscriptKey, ToPublicKey}; +use bitcoin::blockdata::{opcodes, script::Builder}; +use bitcoin::hashes::{ + borrow_slice_impl, hex_fmt_impl, index_impl, serde_impl, sha256t_hash_newtype, Hash, +}; +use bitcoin::secp256k1::{Secp256k1, Signature}; +use bitcoin::{OutPoint, PublicKey, SigHashType, Transaction, TxIn, TxOut}; + +use crate::interpreter::{Error as InterpreterError, Interpreter}; +use std::convert::From; + +// BIP322 message tag = sha256("BIP0322-signed-message") +static MIDSTATE: [u8; 32] = [ + 116, 101, 132, 161, 135, 47, 161, 0, 65, 85, 78, 255, 160, 56, 214, 18, 73, 66, 221, 121, 180, + 229, 138, 76, 218, 24, 78, 19, 219, 230, 44, 73, +]; + +// BIP322 Tagged Hash +sha256t_hash_newtype!( + MessageHash, + MessageTag, + MIDSTATE, + 64, + doc = "test hash", + true +); + +/// BIP322 Error types +#[derive(Debug)] +pub enum BIP322Error { + /// BIP322 Internal Error + InternalError(String), + + /// Signature Validation Error + ValidationError(InterpreterError), +} + +#[doc(hidden)] +impl From<InterpreterError> for BIP322Error { + fn from(e: InterpreterError) -> BIP322Error { + BIP322Error::ValidationError(e) + } +} + +/// Bip322 Signatures +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Bip322Signature { + /// Legacy style. Only applicable for P2PKH message_challenge + Legacy(Signature, PublicKey), + + /// Simple witness structure + Simple(Vec<Vec<u8>>), + + /// Full `to_sign` transaction structure + Full(Transaction), +} + +/// BIP322 validator structure +/// A standard for interoperable signed messages based on the Bitcoin Script format, +/// either for proving fund availability, or committing to a message as the intended +/// recipient of funds sent to the invoice address. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Bip322<T: MiniscriptKey + ToPublicKey> { + /// Message to be signed + message: Vec<u8>, + + /// Signature to verify the message + /// Optional value is used here because a validator structure can be + /// created without a BIP322Signature. Such structure can only produce + /// to_spend (or empty to_sign) transaction, but cannot validate them. + signature: Option<Bip322Signature>, + + /// script_pubkey to define the challenge script inside to_spend transaction + /// here we take in descriptors to derive the resulting script_pubkey + message_challenge: Descriptor<T>, +} + +impl<T: MiniscriptKey + ToPublicKey> Bip322<T> { + /// Create a new BIP322 validator + pub fn new(msg: &[u8], sig: Option<Bip322Signature>, addr: Descriptor<T>) -> Self { + Bip322 { + message: msg.to_vec(), + signature: sig, + message_challenge: addr, + } + } + + /// Insert Signature inside BIP322 structure + pub fn insert_sig(&mut self, sig: Bip322Signature) { + self.signature = Some(sig) + } + + /// create the to_spend transaction + pub fn to_spend(&self) -> Transaction { + // create default input and output + let mut vin = TxIn::default(); + let mut vout = TxOut::default(); + + // calculate the message tagged hash + let msg_hash = MessageHash::hash(&self.message[..]).into_inner(); + + // mutate the input with appropriate script_sig and sequence + vin.script_sig = Builder::new() + .push_int(0) + .push_slice(&msg_hash[..]) + .into_script(); + vin.sequence = 0; + + // mutate the value and script_pubkey as appropriate + vout.value = 0; + vout.script_pubkey = self.message_challenge.script_pubkey(); + + // create and return final transaction + Transaction { + version: 0, + lock_time: 0, + input: vec![vin], + output: vec![vout], + } + } + + /// Create to_sign transaction + /// This will create a transaction structure with empty signature and witness field + /// its up to the user of the library to fill the Tx with appropriate signature and witness + pub fn to_sign(&self) -> Transaction { + // create the appropriate input + let outpoint = OutPoint::new(self.to_spend().txid(), 0); + let mut input = TxIn::default(); + input.previous_output = outpoint; + input.sequence = 0; + + // create the output + let output = TxOut { + value: 0, + script_pubkey: Builder::new() + .push_opcode(opcodes::all::OP_RETURN) + .into_script(), + }; + + // return resulting transaction + Transaction { + version: 0, + lock_time: 0, + input: vec![input], + output: vec![output], + } + } + + /// Validate a BIP322 Signature against the message and challenge script + /// This will require a BIP322Signature inside the structure + pub fn validate(&self) -> Result<bool, BIP322Error> { + match &self.signature { + None => Err(BIP322Error::InternalError( + "Signature required for validation".to_string(), + )), + Some(sig) => { + match sig { + // A Full signature can be validated directly against the `to_sign` transaction + Bip322Signature::Full(to_sign) => self.tx_validation(to_sign), + + // If Simple Signature is provided, the resulting `to_sign` Tx will be computed + Bip322Signature::Simple(witness) => { + // create empty to_sign transaction + let mut to_sign = self.to_sign(); + + to_sign.input[0].witness = witness.to_owned(); + + self.tx_validation(&to_sign) + } + + // Legacy Signature can only be used to validate against P2PKH message_challenge + Bip322Signature::Legacy(sig, pubkey) => { + if !self.message_challenge.script_pubkey().is_p2pkh() { + return Err(BIP322Error::InternalError("Legacy style signature is only applicable for P2PKH message_challenge".to_string())); + } else { + let mut sig_ser = sig.serialize_der()[..].to_vec(); + + // By default SigHashType is ALL + sig_ser.push(SigHashType::All as u8); + + let script_sig = Builder::new() + .push_slice(&sig_ser[..]) + .push_key(&pubkey) + .into_script(); + + let mut to_sign = self.to_sign(); + + to_sign.input[0].script_sig = script_sig; + + self.tx_validation(&to_sign) + } + } + } + } + } + } + + // Internal helper function to perform transaction validation + fn tx_validation(&self, to_sign: &Transaction) -> Result<bool, BIP322Error> { + let secp = Secp256k1::new(); + + // create an Interpreter to validate to_spend transaction + let mut interpreter = Interpreter::from_txdata( + &self.message_challenge.script_pubkey(), + &to_sign.input[0].script_sig, + &to_sign.input[0].witness, + 0, + 0, + )?; + + // create the signature verification function + let vfyfn = interpreter.sighash_verify(&secp, &to_sign, 0, 0); + + let mut result = false; + + for elem in interpreter.iter(vfyfn) { + match elem { + Ok(_) => result = true, + Err(e) => return Err(BIP322Error::ValidationError(e)), + } + } + + Ok(result) + } +} + +#[cfg(test)] +mod test { + use super::*; + use bitcoin::secp256k1::{Message, Secp256k1}; + use bitcoin::util::bip143; + use bitcoin::PrivateKey; + use bitcoin::SigHashType; + + #[test] + fn test_bip322_validation() { + // Create key pairs and secp context + let sk = + PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap(); + + let ctx = Secp256k1::new(); + let pk = sk.public_key(&ctx); + + // wpkh descriptor from pubkey + let desc = Descriptor::new_wpkh(pk).unwrap(); + + // Corresponding p2pkh script. used for sighash calculation + let p2pkh_script = bitcoin::Script::new_p2pkh(&pk.pubkey_hash()); + + // Create BIP322 structures with empty signature + let mut bip322_1 = Bip322 { + message: b"Hello World".to_vec(), + message_challenge: desc.clone(), + signature: None, + }; + let mut bip322_2 = bip322_1.clone(); + let mut bip322_3 = bip322_1.clone(); + + // -------------------------------------------------------------- + // Check BIP322Signature::FUll + + // Generate to_sign transaction + let mut to_sign = bip322_1.to_sign(); + + // Generate witness for above wpkh pubkey + let mut sighash_cache = bip143::SigHashCache::new(&to_sign); + let message = sighash_cache.signature_hash(0, &p2pkh_script, 0, SigHashType::All.into()); + let message = Message::from_slice(&message[..]).unwrap(); + + let signature = ctx.sign(&message, &sk.key); + let der = signature.serialize_der(); + let mut sig_with_hash = der[..].to_vec(); + sig_with_hash.push(SigHashType::All as u8); + + let witness: Vec<Vec<u8>> = vec![sig_with_hash, pk.to_bytes()]; + to_sign.input[0].witness = witness.clone(); + + // Insert signature inside BIP322 structure + let bip322_signature = Bip322Signature::Full(to_sign); + bip322_1.insert_sig(bip322_signature); + + // Check validation + assert_eq!(bip322_1.validate().unwrap(), true); + + // ------------------------------------------------------------ + // Check Bip322Signature::Simple + + // Same structure can be validated with Simple type signature + bip322_2.insert_sig(Bip322Signature::Simple(witness)); + + assert_eq!(bip322_2.validate().unwrap(), true); + + // ------------------------------------------------------------ + // Check Bip322Signature::Legacy + + let desc = Descriptor::new_pkh(pk); + + // Replace previous message_challenge with p2pkh + bip322_3.message_challenge = desc.clone(); + + // Create empty to_sign + let to_sign = bip322_3.to_sign(); + + // Compute SigHash and Signature + let message = to_sign.signature_hash(0, &desc.script_pubkey(), SigHashType::All as u32); + let message = Message::from_slice(&message[..]).unwrap(); + let signature = ctx.sign(&message, &sk.key); + + // Create Bip322Signature::Legacy + let bip322_sig = Bip322Signature::Legacy(signature, pk); + bip322_3.insert_sig(bip322_sig); + + // Check validation + assert_eq!(bip322_3.validate().unwrap(), true); + } +} diff --git a/src/lib.rs b/src/lib.rs index 6de2ea346..2b227c399 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,6 +108,7 @@ extern crate test; #[macro_use] mod macros; +pub mod bip322; pub mod descriptor; pub mod expression; pub mod interpreter; From 46b17e553e5885b98860a5950a05e8b0b6d11ad0 Mon Sep 17 00:00:00 2001 From: codeShark149 <rajarshi149@gmail.com> Date: Wed, 17 Mar 2021 00:04:42 +0530 Subject: [PATCH 2/2] Review comment updates 1 --- src/bip322.rs | 251 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 164 insertions(+), 87 deletions(-) diff --git a/src/bip322.rs b/src/bip322.rs index 1ae1b0148..f3f2d4ccf 100644 --- a/src/bip322.rs +++ b/src/bip322.rs @@ -1,5 +1,5 @@ // BIP322 Generic Signature Algorithm -// Written in 2019 by +// Written in 2021 by // Rajarshi Maitra <rajarshi149@protonmail.com>] // // To the extent possible under law, the author(s) have dedicated all @@ -19,7 +19,7 @@ //! `https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki` //! -use crate::{Descriptor, DescriptorTrait, MiniscriptKey, ToPublicKey}; +use super::{Descriptor, DescriptorTrait, MiniscriptKey, ToPublicKey}; use bitcoin::blockdata::{opcodes, script::Builder}; use bitcoin::hashes::{ borrow_slice_impl, hex_fmt_impl, index_impl, serde_impl, sha256t_hash_newtype, Hash, @@ -27,13 +27,13 @@ use bitcoin::hashes::{ use bitcoin::secp256k1::{Secp256k1, Signature}; use bitcoin::{OutPoint, PublicKey, SigHashType, Transaction, TxIn, TxOut}; -use crate::interpreter::{Error as InterpreterError, Interpreter}; +use super::interpreter::{Error as InterpreterError, Interpreter}; use std::convert::From; -// BIP322 message tag = sha256("BIP0322-signed-message") -static MIDSTATE: [u8; 32] = [ - 116, 101, 132, 161, 135, 47, 161, 0, 65, 85, 78, 255, 160, 56, 214, 18, 73, 66, 221, 121, 180, - 229, 138, 76, 218, 24, 78, 19, 219, 230, 44, 73, +// BIP322 message tagged hash midstate +const MIDSTATE: [u8; 32] = [ + 137, 110, 101, 166, 158, 24, 33, 51, 154, 160, 217, 89, 167, 185, 222, 252, 115, 60, 186, 140, + 151, 47, 2, 20, 94, 72, 184, 111, 248, 59, 249, 156, ]; // BIP322 Tagged Hash @@ -42,7 +42,7 @@ sha256t_hash_newtype!( MessageTag, MIDSTATE, 64, - doc = "test hash", + doc = "BIP322 message tagged hash", true ); @@ -76,41 +76,50 @@ pub enum Bip322Signature { Full(Transaction), } +/// TODO: Bip322 Signer structure +pub struct Bip322Signer {} + /// BIP322 validator structure /// A standard for interoperable signed messages based on the Bitcoin Script format, /// either for proving fund availability, or committing to a message as the intended /// recipient of funds sent to the invoice address. #[derive(Debug, Clone, Eq, PartialEq)] -pub struct Bip322<T: MiniscriptKey + ToPublicKey> { +pub struct Bip322Validator<Pk: MiniscriptKey + ToPublicKey> { /// Message to be signed - message: Vec<u8>, + message: String, /// Signature to verify the message - /// Optional value is used here because a validator structure can be - /// created without a BIP322Signature. Such structure can only produce - /// to_spend (or empty to_sign) transaction, but cannot validate them. - signature: Option<Bip322Signature>, + signature: Bip322Signature, /// script_pubkey to define the challenge script inside to_spend transaction /// here we take in descriptors to derive the resulting script_pubkey - message_challenge: Descriptor<T>, + message_challenge: Descriptor<Pk>, + + /// Age + age: u32, + + /// Height + height: u32, } -impl<T: MiniscriptKey + ToPublicKey> Bip322<T> { +impl<Pk: MiniscriptKey + ToPublicKey> Bip322Validator<Pk> { /// Create a new BIP322 validator - pub fn new(msg: &[u8], sig: Option<Bip322Signature>, addr: Descriptor<T>) -> Self { - Bip322 { - message: msg.to_vec(), + pub fn new( + msg: String, + sig: Bip322Signature, + addr: Descriptor<Pk>, + age: u32, + height: u32, + ) -> Self { + Bip322Validator { + message: msg, signature: sig, message_challenge: addr, + age: age, + height: height, } } - /// Insert Signature inside BIP322 structure - pub fn insert_sig(&mut self, sig: Bip322Signature) { - self.signature = Some(sig) - } - /// create the to_spend transaction pub fn to_spend(&self) -> Transaction { // create default input and output @@ -118,7 +127,7 @@ impl<T: MiniscriptKey + ToPublicKey> Bip322<T> { let mut vout = TxOut::default(); // calculate the message tagged hash - let msg_hash = MessageHash::hash(&self.message[..]).into_inner(); + let msg_hash = MessageHash::hash(&self.message.as_bytes()).into_inner(); // mutate the input with appropriate script_sig and sequence vin.script_sig = Builder::new() @@ -143,12 +152,12 @@ impl<T: MiniscriptKey + ToPublicKey> Bip322<T> { /// Create to_sign transaction /// This will create a transaction structure with empty signature and witness field /// its up to the user of the library to fill the Tx with appropriate signature and witness - pub fn to_sign(&self) -> Transaction { + pub fn empty_to_sign(&self) -> Transaction { // create the appropriate input let outpoint = OutPoint::new(self.to_spend().txid(), 0); let mut input = TxIn::default(); input.previous_output = outpoint; - input.sequence = 0; + input.sequence = self.height; // create the output let output = TxOut { @@ -160,8 +169,8 @@ impl<T: MiniscriptKey + ToPublicKey> Bip322<T> { // return resulting transaction Transaction { - version: 0, - lock_time: 0, + version: 2, + lock_time: self.age, input: vec![input], output: vec![output], } @@ -171,46 +180,46 @@ impl<T: MiniscriptKey + ToPublicKey> Bip322<T> { /// This will require a BIP322Signature inside the structure pub fn validate(&self) -> Result<bool, BIP322Error> { match &self.signature { - None => Err(BIP322Error::InternalError( - "Signature required for validation".to_string(), - )), - Some(sig) => { - match sig { - // A Full signature can be validated directly against the `to_sign` transaction - Bip322Signature::Full(to_sign) => self.tx_validation(to_sign), - - // If Simple Signature is provided, the resulting `to_sign` Tx will be computed - Bip322Signature::Simple(witness) => { - // create empty to_sign transaction - let mut to_sign = self.to_sign(); - - to_sign.input[0].witness = witness.to_owned(); - - self.tx_validation(&to_sign) - } - - // Legacy Signature can only be used to validate against P2PKH message_challenge - Bip322Signature::Legacy(sig, pubkey) => { - if !self.message_challenge.script_pubkey().is_p2pkh() { - return Err(BIP322Error::InternalError("Legacy style signature is only applicable for P2PKH message_challenge".to_string())); - } else { - let mut sig_ser = sig.serialize_der()[..].to_vec(); - - // By default SigHashType is ALL - sig_ser.push(SigHashType::All as u8); - - let script_sig = Builder::new() - .push_slice(&sig_ser[..]) - .push_key(&pubkey) - .into_script(); - - let mut to_sign = self.to_sign(); - - to_sign.input[0].script_sig = script_sig; - - self.tx_validation(&to_sign) - } - } + // A Full signature can be validated directly against the `to_sign` transaction + Bip322Signature::Full(to_sign) => self.tx_validation(to_sign), + + // If Simple Signature is provided, the resulting `to_sign` Tx will be computed + Bip322Signature::Simple(witness) => { + if !self.message_challenge.script_pubkey().is_witness_program() { + return Err(BIP322Error::InternalError("Simple style signature is only applicable for Segwit type message_challenge".to_string())); + } else { + // create empty to_sign transaction + let mut to_sign = self.empty_to_sign(); + + to_sign.input[0].witness = witness.to_owned(); + + self.tx_validation(&to_sign) + } + } + + // Legacy Signature can only be used to validate against P2PKH message_challenge + Bip322Signature::Legacy(sig, pubkey) => { + if !self.message_challenge.script_pubkey().is_p2pkh() { + return Err(BIP322Error::InternalError( + "Legacy style signature is only applicable for P2PKH message_challenge" + .to_string(), + )); + } else { + let mut sig_ser = sig.serialize_der()[..].to_vec(); + + // By default SigHashType is ALL + sig_ser.push(SigHashType::All as u8); + + let script_sig = Builder::new() + .push_slice(&sig_ser[..]) + .push_key(&pubkey) + .into_script(); + + let mut to_sign = self.empty_to_sign(); + + to_sign.input[0].script_sig = script_sig; + + self.tx_validation(&to_sign) } } } @@ -248,6 +257,8 @@ impl<T: MiniscriptKey + ToPublicKey> Bip322<T> { #[cfg(test)] mod test { use super::*; + use bitcoin::hashes::sha256t::Tag; + use bitcoin::hashes::{sha256, HashEngine}; use bitcoin::secp256k1::{Message, Secp256k1}; use bitcoin::util::bip143; use bitcoin::PrivateKey; @@ -268,23 +279,69 @@ mod test { // Corresponding p2pkh script. used for sighash calculation let p2pkh_script = bitcoin::Script::new_p2pkh(&pk.pubkey_hash()); - // Create BIP322 structures with empty signature - let mut bip322_1 = Bip322 { - message: b"Hello World".to_vec(), - message_challenge: desc.clone(), - signature: None, + let message = "Hello World".to_string(); + let age = 0; + let height = 0; + + // Create to_spend transaction + let to_spend = { + // create default input and output + let mut vin = TxIn::default(); + let mut vout = TxOut::default(); + + // calculate the message tagged hash + let msg_hash = MessageHash::hash(&message.as_bytes()).into_inner(); + + // mutate the input with appropriate script_sig and sequence + vin.script_sig = Builder::new() + .push_int(0) + .push_slice(&msg_hash[..]) + .into_script(); + vin.sequence = 0; + + // mutate the value and script_pubkey as appropriate + vout.value = 0; + vout.script_pubkey = desc.script_pubkey(); + + // create and return final transaction + Transaction { + version: 0, + lock_time: 0, + input: vec![vin], + output: vec![vout], + } + }; + + // create an empty to_sign transaction + let mut empty_to_sign = { + // create the appropriate input + let outpoint = OutPoint::new(to_spend.txid(), 0); + let mut input = TxIn::default(); + input.previous_output = outpoint; + input.sequence = height; + + // create the output + let output = TxOut { + value: 0, + script_pubkey: Builder::new() + .push_opcode(opcodes::all::OP_RETURN) + .into_script(), + }; + + // return resulting transaction + Transaction { + version: 2, + lock_time: age, + input: vec![input], + output: vec![output], + } }; - let mut bip322_2 = bip322_1.clone(); - let mut bip322_3 = bip322_1.clone(); // -------------------------------------------------------------- // Check BIP322Signature::FUll - // Generate to_sign transaction - let mut to_sign = bip322_1.to_sign(); - // Generate witness for above wpkh pubkey - let mut sighash_cache = bip143::SigHashCache::new(&to_sign); + let mut sighash_cache = bip143::SigHashCache::new(&empty_to_sign); let message = sighash_cache.signature_hash(0, &p2pkh_script, 0, SigHashType::All.into()); let message = Message::from_slice(&message[..]).unwrap(); @@ -294,11 +351,20 @@ mod test { sig_with_hash.push(SigHashType::All as u8); let witness: Vec<Vec<u8>> = vec![sig_with_hash, pk.to_bytes()]; - to_sign.input[0].witness = witness.clone(); + empty_to_sign.input[0].witness = witness.clone(); - // Insert signature inside BIP322 structure - let bip322_signature = Bip322Signature::Full(to_sign); - bip322_1.insert_sig(bip322_signature); + let bip322_signature = Bip322Signature::Full(empty_to_sign); + + // Create BIP322 Validator + let bip322_1 = Bip322Validator { + message: "Hello World".to_string(), + message_challenge: desc.clone(), + signature: bip322_signature, + age: 0, + height: 0, + }; + let mut bip322_2 = bip322_1.clone(); + let mut bip322_3 = bip322_1.clone(); // Check validation assert_eq!(bip322_1.validate().unwrap(), true); @@ -307,7 +373,7 @@ mod test { // Check Bip322Signature::Simple // Same structure can be validated with Simple type signature - bip322_2.insert_sig(Bip322Signature::Simple(witness)); + bip322_2.signature = Bip322Signature::Simple(witness); assert_eq!(bip322_2.validate().unwrap(), true); @@ -320,7 +386,7 @@ mod test { bip322_3.message_challenge = desc.clone(); // Create empty to_sign - let to_sign = bip322_3.to_sign(); + let to_sign = bip322_3.empty_to_sign(); // Compute SigHash and Signature let message = to_sign.signature_hash(0, &desc.script_pubkey(), SigHashType::All as u32); @@ -329,9 +395,20 @@ mod test { // Create Bip322Signature::Legacy let bip322_sig = Bip322Signature::Legacy(signature, pk); - bip322_3.insert_sig(bip322_sig); + bip322_3.signature = bip322_sig; // Check validation assert_eq!(bip322_3.validate().unwrap(), true); } + + #[test] + fn test_tagged_hash() { + let mut engine = sha256::Hash::engine(); + let tag_hash = sha256::Hash::hash("BIP0322-signed-message".as_bytes()); + engine.input(&tag_hash[..]); + engine.input(&tag_hash[..]); + + assert_eq!(engine.midstate().into_inner(), MIDSTATE); + assert_eq!(engine.midstate(), MessageTag::engine().midstate()); + } }