diff --git a/bitcoin/src/crypto/ecdsa.rs b/bitcoin/src/crypto/ecdsa.rs index 162a831f3..b29c64e1b 100644 --- a/bitcoin/src/crypto/ecdsa.rs +++ b/bitcoin/src/crypto/ecdsa.rs @@ -7,6 +7,8 @@ use core::str::FromStr; use core::{fmt, iter}; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Unstructured}; use hex::FromHex; use internals::{impl_to_hex_from_lower_hex, write_err}; use io::Write; @@ -293,6 +295,41 @@ impl From for ParseSignatureError { fn from(e: DecodeError) -> Self { Self::Decode(e) } } +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Signature { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + // The valid range of r and s should be between 0 and n-1 where + // n = 0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141 + let high_min = 0x0u128; + let high_max = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEu128; + let low_min = 0x0u128; + let low_max = 0xBAAEDCE6AF48A03BBFD25E8CD0364140u128; + + // Equally weight the chances of getting a minimum value for a signature, maximum value for + // a signature, and an arbitrary valid signature + let choice = u.int_in_range(0..=2)?; + let (high, low) = match choice { + 0 => (high_min, low_min), + 1 => (high_max, low_max), + _ => (u.int_in_range(high_min..=high_max)?, u.int_in_range(low_min..=low_max)?), + }; + + // We can use the same bytes for r and s since they're just arbitrary values + let mut bytes: [u8; 32] = [0; 32]; + bytes[..16].copy_from_slice(&high.to_be_bytes()); + bytes[16..].copy_from_slice(&low.to_be_bytes()); + + let mut signature_bytes: [u8; 64] = [0; 64]; + signature_bytes[..32].copy_from_slice(&bytes); + signature_bytes[32..].copy_from_slice(&bytes); + + Ok(Signature{ + signature: secp256k1::ecdsa::Signature::from_compact(&signature_bytes).unwrap(), + sighash_type: EcdsaSighashType::arbitrary(u)?, + }) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs index ab0ac6287..53b7e0dcc 100644 --- a/bitcoin/src/crypto/sighash.rs +++ b/bitcoin/src/crypto/sighash.rs @@ -13,6 +13,8 @@ use core::{fmt, str}; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Unstructured}; use hashes::{hash_newtype, sha256, sha256d, sha256t, sha256t_tag}; use internals::write_err; use io::Write; @@ -1467,6 +1469,37 @@ impl std::error::Error for SigningDataError { } } +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for EcdsaSighashType { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=5)?; + match choice { + 0 => Ok(EcdsaSighashType::All), + 1 => Ok(EcdsaSighashType::None), + 2 => Ok(EcdsaSighashType::Single), + 3 => Ok(EcdsaSighashType::AllPlusAnyoneCanPay), + 4 => Ok(EcdsaSighashType::NonePlusAnyoneCanPay), + _ => Ok(EcdsaSighashType::SinglePlusAnyoneCanPay) + } + } +} + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for TapSighashType { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let choice = u.int_in_range(0..=6)?; + match choice { + 0 => Ok(TapSighashType::Default), + 1 => Ok(TapSighashType::All), + 2 => Ok(TapSighashType::None), + 3 => Ok(TapSighashType::Single), + 4 => Ok(TapSighashType::AllPlusAnyoneCanPay), + 5 => Ok(TapSighashType::NonePlusAnyoneCanPay), + _ => Ok(TapSighashType::SinglePlusAnyoneCanPay) + } + } +} + #[cfg(test)] mod tests { use hashes::HashEngine; diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs index 7bbb6e47b..de58eb591 100644 --- a/bitcoin/src/crypto/taproot.rs +++ b/bitcoin/src/crypto/taproot.rs @@ -6,6 +6,8 @@ use core::fmt; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Unstructured}; use internals::write_err; use io::Write; @@ -133,3 +135,15 @@ impl From for SigFromSliceError { impl From for SigFromSliceError { fn from(err: InvalidSighashTypeError) -> Self { Self::SighashType(err) } } + +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for Signature { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let arbitrary_bytes: [u8; secp256k1::constants::SCHNORR_SIGNATURE_SIZE] = u.arbitrary()?; + + Ok(Signature { + signature: secp256k1::schnorr::Signature::from_slice(&arbitrary_bytes).unwrap(), + sighash_type: TapSighashType::arbitrary(u)?, + }) + } +}