Merge rust-bitcoin/rust-bitcoin#776: Change EcdsaSig hash type deser in psbt
abe52f681b
Cleanup/Dedup psbt (De)Serialization code (sanket1729)fbd86dcf63
Update documentation of EcdsaSig::from_slice (sanket1729)85009a7b50
Update documentation of from_u32_consensus (sanket1729)0fed04e2d5
Change EcdsaSig hash type deser (sanket1729) Pull request description: Changes the parsing behavior in PSBT on non-standard sighash types to give an explicit error, rather than silently mangling the parsed value ACKs for top commit: dr-orlovsky: ACKabe52f681b
apoelstra: ACKabe52f681b
Kixunil: ACKabe52f681b
Tree-SHA512: 1d5dbe3aa5885ca16649cf8ea05a7476e8dd977dd870b79358d97a3ce383bee93754d2b88163e7db3792cdc4b9cb867356409c8eea4e110877577ad196ba0786
This commit is contained in:
commit
d5686ee01d
|
@ -758,6 +758,10 @@ impl EcdsaSigHashType {
|
|||
///
|
||||
/// **Note**: this replicates consensus behaviour, for current standardness rules correctness
|
||||
/// you probably want [Self::from_u32_standard].
|
||||
/// This might cause unexpected behavior because it does not roundtrip. That is,
|
||||
/// `EcdsaSigHashType::from_u32_consensus(n) as u32 != n` for non-standard values of
|
||||
/// `n`. While verifying signatures, the user should retain the `n` and use it compute the
|
||||
/// signature hash message.
|
||||
pub fn from_u32_consensus(n: u32) -> EcdsaSigHashType {
|
||||
// In Bitcoin Core, the SignatureHash function will mask the (int32) value with
|
||||
// 0x1f to (apparently) deactivate ACP when checking for SINGLE and NONE bits.
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
use prelude::*;
|
||||
use core::str::FromStr;
|
||||
use core::fmt;
|
||||
use core::{fmt, iter};
|
||||
use hashes::hex::{self, FromHex};
|
||||
use blockdata::transaction::NonStandardSigHashType;
|
||||
use secp256k1;
|
||||
|
@ -43,7 +43,7 @@ impl EcdsaSig {
|
|||
}
|
||||
}
|
||||
|
||||
/// Deserialize from slice
|
||||
/// Deserialize from slice following the standardness rules for [`EcdsaSigHashType`]
|
||||
pub fn from_slice(sl: &[u8]) -> Result<Self, EcdsaSigError> {
|
||||
let (hash_ty, sig) = sl.split_last()
|
||||
.ok_or(EcdsaSigError::EmptySignature)?;
|
||||
|
@ -57,9 +57,10 @@ impl EcdsaSig {
|
|||
/// Serialize EcdsaSig
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
// TODO: add support to serialize to a writer to SerializedSig
|
||||
let mut ser_sig = self.sig.serialize_der().to_vec();
|
||||
ser_sig.push(self.hash_ty.as_u32() as u8);
|
||||
ser_sig
|
||||
self.sig.serialize_der()
|
||||
.iter().map(|x| *x)
|
||||
.chain(iter::once(self.hash_ty as u8))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -823,7 +823,7 @@ mod tests {
|
|||
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a075701172102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232000000").unwrap_err();
|
||||
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
||||
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011342173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc34751701aa000000").unwrap_err();
|
||||
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature len");
|
||||
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature length");
|
||||
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757221602fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000000000").unwrap_err();
|
||||
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
||||
let err = hex_psbt!("70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000001052102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa23200").unwrap_err();
|
||||
|
@ -833,9 +833,9 @@ mod tests {
|
|||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6924214022cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094089756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000").unwrap_err();
|
||||
assert_eq!(err.to_string(), "PSBT error: Hash Parse Error: bad slice length 33 (expected 32)");
|
||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094289756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb01010000").unwrap_err();
|
||||
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature len");
|
||||
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature length");
|
||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b093989756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000").unwrap_err();
|
||||
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature len");
|
||||
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature length");
|
||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926315c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f80023202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
||||
assert_eq!(err.to_string(), "parse failed: Invalid control block");
|
||||
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926115c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e123202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
||||
|
|
|
@ -24,18 +24,19 @@ use io;
|
|||
|
||||
use blockdata::script::Script;
|
||||
use blockdata::witness::Witness;
|
||||
use blockdata::transaction::{EcdsaSigHashType, Transaction, TxOut};
|
||||
use blockdata::transaction::{Transaction, TxOut};
|
||||
use consensus::encode::{self, serialize, Decodable, Encodable, deserialize_partial};
|
||||
use secp256k1::{self, XOnlyPublicKey};
|
||||
use util::bip32::{ChildNumber, Fingerprint, KeySource};
|
||||
use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
||||
use util::ecdsa::EcdsaSig;
|
||||
use util::ecdsa::{EcdsaSig, EcdsaSigError};
|
||||
use util::psbt;
|
||||
use util::taproot::{TapBranchHash, TapLeafHash, ControlBlock, LeafVersion};
|
||||
use schnorr;
|
||||
|
||||
use super::map::{TapTree, PsbtSigHashType};
|
||||
|
||||
use util::taproot::TaprootBuilder;
|
||||
use util::sighash::SchnorrSigHashType;
|
||||
/// A trait for serializing a value as raw data for insertion into PSBT
|
||||
/// key-value pairs.
|
||||
pub trait Serialize {
|
||||
|
@ -89,25 +90,35 @@ impl Deserialize for secp256k1::PublicKey {
|
|||
|
||||
impl Serialize for EcdsaSig {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let mut buf = Vec::with_capacity(72);
|
||||
buf.extend(self.sig.serialize_der().iter());
|
||||
buf.push(self.hash_ty as u8);
|
||||
buf
|
||||
self.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for EcdsaSig {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
|
||||
let (sighash_byte, signature) = bytes.split_last()
|
||||
.ok_or(encode::Error::ParseFailed("empty partial signature data"))?;
|
||||
Ok(EcdsaSig {
|
||||
sig: secp256k1::ecdsa::Signature::from_der(signature)
|
||||
.map_err(|_| encode::Error::ParseFailed("non-DER encoded signature"))?,
|
||||
// NB: Since BIP-174 says "the signature as would be pushed to the stack from
|
||||
// a scriptSig or witness" we should use a consensus deserialization and do
|
||||
// not error on a non-standard values.
|
||||
hash_ty: EcdsaSigHashType::from_u32_consensus(*sighash_byte as u32)
|
||||
})
|
||||
// NB: Since BIP-174 says "the signature as would be pushed to the stack from
|
||||
// a scriptSig or witness" we should ideally use a consensus deserialization and do
|
||||
// not error on a non-standard values. However,
|
||||
//
|
||||
// 1) the current implementation of from_u32_consensus(`flag`) does not preserve
|
||||
// the sighash byte `flag` mapping all unknown values to EcdsaSighashType::All or
|
||||
// EcdsaSigHashType::AllPlusAnyOneCanPay. Therefore, break the invariant
|
||||
// EcdsaSig::from_slice(&sl[..]).to_vec = sl.
|
||||
//
|
||||
// 2) This would cause to have invalid signatures because the sighash message
|
||||
// also has a field sighash_u32 (See BIP141). For example, when signing with non-standard
|
||||
// 0x05, the sighash message would have the last field as 0x05u32 while, the verification
|
||||
// would use check the signature assuming sighash_u32 as `0x01`.
|
||||
match EcdsaSig::from_slice(&bytes) {
|
||||
Ok(sig) => Ok(sig),
|
||||
Err(EcdsaSigError::EmptySignature) =>
|
||||
Err(encode::Error::ParseFailed("Empty partial signature data")),
|
||||
Err(EcdsaSigError::NonStandardSigHashType(flag)) =>
|
||||
Err(encode::Error::from(psbt::Error::NonStandardSigHashType(flag))),
|
||||
Err(EcdsaSigError::Secp256k1(..)) =>
|
||||
Err(encode::Error::ParseFailed("Invalid Ecdsa signature")),
|
||||
Err(EcdsaSigError::HexEncoding(..)) => unreachable!("Decoding from slice, not hex")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,20 +205,15 @@ impl Serialize for schnorr::SchnorrSig {
|
|||
|
||||
impl Deserialize for schnorr::SchnorrSig {
|
||||
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
|
||||
match bytes.len() {
|
||||
65 => {
|
||||
let hash_ty = SchnorrSigHashType::from_u8(bytes[64])
|
||||
.map_err(|_| encode::Error::ParseFailed("Invalid Sighash type"))?;
|
||||
let sig = secp256k1::schnorr::Signature::from_slice(&bytes[..64])
|
||||
.map_err(|_| encode::Error::ParseFailed("Invalid Schnorr signature"))?;
|
||||
Ok(schnorr::SchnorrSig{ sig, hash_ty })
|
||||
match schnorr::SchnorrSig::from_slice(&bytes) {
|
||||
Ok(sig) => Ok(sig),
|
||||
Err(schnorr::SchnorrSigError::InvalidSighashType(flag)) => {
|
||||
Err(encode::Error::from(psbt::Error::NonStandardSigHashType(flag as u32)))
|
||||
}
|
||||
64 => {
|
||||
let sig = secp256k1::schnorr::Signature::from_slice(&bytes[..64])
|
||||
.map_err(|_| encode::Error::ParseFailed("Invalid Schnorr signature"))?;
|
||||
Ok(schnorr::SchnorrSig{ sig, hash_ty: SchnorrSigHashType::Default })
|
||||
}
|
||||
_ => Err(encode::Error::ParseFailed("Invalid Schnorr signature len"))
|
||||
Err(schnorr::SchnorrSigError::InvalidSchnorrSigSize(_)) =>
|
||||
Err(encode::Error::ParseFailed("Invalid Schnorr signature length")),
|
||||
Err(schnorr::SchnorrSigError::Secp256k1(..)) =>
|
||||
Err(encode::Error::ParseFailed("Invalid Schnorr signature")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue