sapio_bitcoin/util/
misc.rs

1// Rust Bitcoin Library
2// Written in 2014 by
3//     Andrew Poelstra <apoelstra@wpsoftware.net>
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the CC0 Public Domain Dedication
11// along with this software.
12// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13//
14
15//! Miscellaneous functions.
16//!
17//! This module provides various utility functions including secp256k1 signature
18//! recovery when library is used with the `secp-recovery` feature.
19//!
20
21use prelude::*;
22
23use hashes::{sha256d, Hash, HashEngine};
24
25use blockdata::opcodes;
26use consensus::{encode, Encodable};
27
28#[cfg(feature = "secp-recovery")]
29#[cfg_attr(docsrs, doc(cfg(feature = "secp-recovery")))]
30pub use self::message_signing::{MessageSignature, MessageSignatureError};
31
32/// The prefix for signed messages using Bitcoin's message signing protocol.
33pub const BITCOIN_SIGNED_MSG_PREFIX: &[u8] = b"\x18Bitcoin Signed Message:\n";
34
35#[cfg(feature = "secp-recovery")]
36mod message_signing {
37    #[cfg(feature = "base64")] use prelude::*;
38    use core::fmt;
39    #[cfg(feature = "std")] use std::error;
40
41    use hashes::sha256d;
42    use secp256k1;
43    use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
44
45    use util::key::PublicKey;
46    use util::address::{Address, AddressType};
47
48    /// An error used for dealing with Bitcoin Signed Messages.
49    #[cfg_attr(docsrs, doc(cfg(feature = "secp-recovery")))]
50    #[derive(Debug, PartialEq, Eq)]
51    pub enum MessageSignatureError {
52        /// Signature is expected to be 65 bytes.
53        InvalidLength,
54        /// The signature is invalidly constructed.
55        InvalidEncoding(secp256k1::Error),
56        /// Invalid base64 encoding.
57        InvalidBase64,
58        /// Unsupported Address Type
59        UnsupportedAddressType(AddressType),
60    }
61
62    impl fmt::Display for MessageSignatureError {
63        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64            match *self {
65                MessageSignatureError::InvalidLength => write!(f, "length not 65 bytes"),
66                MessageSignatureError::InvalidEncoding(ref e) => write!(f, "invalid encoding: {}", e),
67                MessageSignatureError::InvalidBase64 => write!(f, "invalid base64"),
68                MessageSignatureError::UnsupportedAddressType(ref address_type) => write!(f, "unsupported address type: {}", address_type),
69            }
70        }
71    }
72
73    #[cfg(feature = "std")]
74    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
75    impl error::Error for MessageSignatureError {
76        fn cause(&self) -> Option<&dyn error::Error> {
77            match *self {
78                MessageSignatureError::InvalidEncoding(ref e) => Some(e),
79                _ => None,
80            }
81        }
82    }
83
84    #[doc(hidden)]
85    impl From<secp256k1::Error> for MessageSignatureError {
86        fn from(e: secp256k1::Error) -> MessageSignatureError {
87            MessageSignatureError::InvalidEncoding(e)
88        }
89    }
90
91    /// A signature on a Bitcoin Signed Message.
92    ///
93    /// In order to use the `to_base64` and `from_base64` methods, as well as the
94    /// `fmt::Display` and `str::FromStr` implementations, the `base64` feature
95    /// must be enabled.
96    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
97    #[cfg_attr(docsrs, doc(cfg(feature = "secp-recovery")))]
98    pub struct MessageSignature {
99        /// The inner recoverable signature.
100        pub signature: RecoverableSignature,
101        /// Whether or not this signature was created with a compressed key.
102        pub compressed: bool,
103    }
104
105    impl MessageSignature {
106        /// Create a new [MessageSignature].
107        pub fn new(signature: RecoverableSignature, compressed: bool) -> MessageSignature {
108            MessageSignature {
109                signature,
110                compressed,
111            }
112        }
113
114        /// Serialize to bytes.
115        pub fn serialize(&self) -> [u8; 65] {
116            let (recid, raw) = self.signature.serialize_compact();
117            let mut serialized = [0u8; 65];
118            serialized[0] = 27;
119            serialized[0] += recid.to_i32() as u8;
120            if self.compressed {
121                serialized[0] += 4;
122            }
123            serialized[1..].copy_from_slice(&raw[..]);
124            serialized
125        }
126
127        /// Create from a byte slice.
128        pub fn from_slice(bytes: &[u8]) -> Result<MessageSignature, MessageSignatureError> {
129            if bytes.len() != 65 {
130                return Err(MessageSignatureError::InvalidLength);
131            }
132            // We just check this here so we can safely subtract further.
133            if bytes[0] < 27 {
134                return Err(MessageSignatureError::InvalidEncoding(secp256k1::Error::InvalidRecoveryId));
135            };
136            let recid = RecoveryId::from_i32(((bytes[0] - 27) & 0x03) as i32)?;
137            Ok(MessageSignature {
138                signature: RecoverableSignature::from_compact(&bytes[1..], recid)?,
139                compressed: ((bytes[0] - 27) & 0x04) != 0,
140            })
141        }
142
143        /// Attempt to recover a public key from the signature and the signed message.
144        ///
145        /// To get the message hash from a message, use [super::signed_msg_hash].
146        pub fn recover_pubkey<C: secp256k1::Verification>(
147            &self,
148            secp_ctx: &secp256k1::Secp256k1<C>,
149            msg_hash: sha256d::Hash
150        ) -> Result<PublicKey, MessageSignatureError> {
151            let msg = secp256k1::Message::from_slice(&msg_hash[..])
152                .expect("cannot fail");
153            let pubkey = secp_ctx.recover_ecdsa(&msg, &self.signature)?;
154            Ok(PublicKey {
155                inner: pubkey,
156                compressed: self.compressed,
157            })
158        }
159
160        /// Verify that the signature signs the message and was signed by the given address.
161        ///
162        /// To get the message hash from a message, use [super::signed_msg_hash].
163        pub fn is_signed_by_address<C: secp256k1::Verification>(
164            &self,
165            secp_ctx: &secp256k1::Secp256k1<C>,
166            address: &Address,
167            msg_hash: sha256d::Hash
168        ) -> Result<bool, MessageSignatureError> {
169            match address.address_type() {
170                Some(AddressType::P2pkh) => {
171                    let pubkey = self.recover_pubkey(secp_ctx, msg_hash)?;
172                    Ok(*address == Address::p2pkh(&pubkey, address.network))
173                }
174                Some(address_type) => Err(MessageSignatureError::UnsupportedAddressType(address_type)),
175                None => Ok(false),
176            }
177        }
178
179        /// Convert a signature from base64 encoding.
180        #[cfg(feature = "base64")]
181        #[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
182        pub fn from_base64(s: &str) -> Result<MessageSignature, MessageSignatureError> {
183            let bytes = ::base64::decode(s).map_err(|_| MessageSignatureError::InvalidBase64)?;
184            MessageSignature::from_slice(&bytes)
185        }
186
187        /// Convert to base64 encoding.
188        #[cfg(feature = "base64")]
189        #[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
190        pub fn to_base64(&self) -> String {
191            ::base64::encode(&self.serialize()[..])
192        }
193    }
194
195    #[cfg(feature = "base64")]
196    #[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
197    impl fmt::Display for MessageSignature {
198        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199            let bytes = self.serialize();
200            // This avoids the allocation of a String.
201            write!(f, "{}", ::base64::display::Base64Display::with_config(
202                    &bytes[..], ::base64::STANDARD))
203        }
204    }
205
206    #[cfg(feature = "base64")]
207    #[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
208    impl ::core::str::FromStr for MessageSignature {
209        type Err = MessageSignatureError;
210        fn from_str(s: &str) -> Result<MessageSignature, MessageSignatureError> {
211            MessageSignature::from_base64(s)
212        }
213    }
214}
215
216/// Search for `needle` in the vector `haystack` and remove every
217/// instance of it, returning the number of instances removed.
218/// Loops through the vector opcode by opcode, skipping pushed data.
219pub fn script_find_and_remove(haystack: &mut Vec<u8>, needle: &[u8]) -> usize {
220    if needle.len() > haystack.len() {
221        return 0;
222    }
223    if needle.is_empty() {
224        return 0;
225    }
226
227    let mut top = haystack.len() - needle.len();
228    let mut n_deleted = 0;
229
230    let mut i = 0;
231    while i <= top {
232        if &haystack[i..(i + needle.len())] == needle {
233            for j in i..top {
234                haystack.swap(j + needle.len(), j);
235            }
236            n_deleted += 1;
237            // This is ugly but prevents infinite loop in case of overflow
238            let overflow = top < needle.len();
239            top = top.wrapping_sub(needle.len());
240            if overflow {
241                break;
242            }
243        } else {
244            i += match opcodes::All::from((*haystack)[i]).classify(opcodes::ClassifyContext::Legacy) {
245                opcodes::Class::PushBytes(n) => n as usize + 1,
246                opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA1) => 2,
247                opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA2) => 3,
248                opcodes::Class::Ordinary(opcodes::Ordinary::OP_PUSHDATA4) => 5,
249                _ => 1
250            };
251        }
252    }
253    haystack.truncate(top.wrapping_add(needle.len()));
254    n_deleted
255}
256
257/// Hash message for signature using Bitcoin's message signing format.
258pub fn signed_msg_hash(msg: &str) -> sha256d::Hash {
259    let mut engine = sha256d::Hash::engine();
260    engine.input(BITCOIN_SIGNED_MSG_PREFIX);
261    let msg_len = encode::VarInt(msg.len() as u64);
262    msg_len.consensus_encode(&mut engine).expect("engines don't error");
263    engine.input(msg.as_bytes());
264    sha256d::Hash::from_engine(engine)
265}
266
267#[cfg(test)]
268mod tests {
269    use super::*;
270    use hashes::hex::ToHex;
271    use super::script_find_and_remove;
272    use super::signed_msg_hash;
273
274    #[test]
275    fn test_script_find_and_remove() {
276        let mut v = vec![101u8, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109];
277
278        assert_eq!(script_find_and_remove(&mut v, &[]), 0);
279        assert_eq!(script_find_and_remove(&mut v, &[105, 105, 105]), 0);
280        assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 105, 106, 107, 108, 109]);
281
282        assert_eq!(script_find_and_remove(&mut v, &[105, 106, 107]), 1);
283        assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103, 104, 108, 109]);
284
285        assert_eq!(script_find_and_remove(&mut v, &[104, 108, 109]), 1);
286        assert_eq!(v, vec![101, 102, 103, 104, 102, 103, 104, 102, 103]);
287
288        assert_eq!(script_find_and_remove(&mut v, &[101]), 1);
289        assert_eq!(v, vec![102, 103, 104, 102, 103, 104, 102, 103]);
290
291        assert_eq!(script_find_and_remove(&mut v, &[102]), 3);
292        assert_eq!(v, vec![103, 104, 103, 104, 103]);
293
294        assert_eq!(script_find_and_remove(&mut v, &[103, 104]), 2);
295        assert_eq!(v, vec![103]);
296
297        assert_eq!(script_find_and_remove(&mut v, &[105, 105, 5]), 0);
298        assert_eq!(script_find_and_remove(&mut v, &[105]), 0);
299        assert_eq!(script_find_and_remove(&mut v, &[103]), 1);
300        assert_eq!(v, Vec::<u8>::new());
301
302        assert_eq!(script_find_and_remove(&mut v, &[105, 105, 5]), 0);
303        assert_eq!(script_find_and_remove(&mut v, &[105]), 0);
304    }
305
306    #[test]
307    fn test_script_codesep_remove() {
308        let mut s = vec![33u8, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 171, 81];
309        assert_eq!(script_find_and_remove(&mut s, &[171]), 2);
310        assert_eq!(s, vec![33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 33, 3, 132, 121, 160, 250, 153, 140, 211, 82, 89, 162, 239, 10, 122, 92, 104, 102, 44, 20, 116, 248, 140, 203, 109, 8, 167, 103, 123, 190, 199, 242, 32, 65, 173, 81]);
311    }
312
313    #[test]
314    fn test_signed_msg_hash() {
315        let hash = signed_msg_hash("test");
316        assert_eq!(hash.to_hex(), "a6f87fe6d58a032c320ff8d1541656f0282c2c7bfcc69d61af4c8e8ed528e49c");
317    }
318
319    #[test]
320    #[cfg(all(feature = "secp-recovery", feature = "base64"))]
321    fn test_message_signature() {
322        use core::str::FromStr;
323        use secp256k1;
324        use ::AddressType;
325
326        let secp = secp256k1::Secp256k1::new();
327        let message = "rust-bitcoin MessageSignature test";
328        let msg_hash = super::signed_msg_hash(&message);
329        let msg = secp256k1::Message::from_slice(&msg_hash).expect("message");
330
331
332        let privkey = secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng());
333        let secp_sig = secp.sign_ecdsa_recoverable(&msg, &privkey);
334        let signature = super::MessageSignature {
335            signature: secp_sig,
336            compressed: true,
337        };
338
339        assert_eq!(signature.to_base64(), signature.to_string());
340        let signature2 = super::MessageSignature::from_str(&signature.to_string()).unwrap();
341        let pubkey = signature2.recover_pubkey(&secp, msg_hash).unwrap();
342        assert_eq!(pubkey.compressed, true);
343        assert_eq!(pubkey.inner, secp256k1::PublicKey::from_secret_key(&secp, &privkey));
344
345        let p2pkh = ::Address::p2pkh(&pubkey, ::Network::Bitcoin);
346        assert_eq!(signature2.is_signed_by_address(&secp, &p2pkh, msg_hash), Ok(true));
347        let p2wpkh = ::Address::p2wpkh(&pubkey, ::Network::Bitcoin).unwrap();
348        assert_eq!(
349            signature2.is_signed_by_address(&secp, &p2wpkh, msg_hash),
350            Err(MessageSignatureError::UnsupportedAddressType(AddressType::P2wpkh))
351        );
352        let p2shwpkh = ::Address::p2shwpkh(&pubkey, ::Network::Bitcoin).unwrap();
353        assert_eq!(
354            signature2.is_signed_by_address(&secp, &p2shwpkh, msg_hash),
355            Err(MessageSignatureError::UnsupportedAddressType(AddressType::P2sh))
356        );
357    }
358
359    #[test]
360    #[cfg(all(feature = "secp-recovery", feature = "base64"))]
361    fn test_incorrect_message_signature() {
362        use secp256k1;
363        use util::key::PublicKey;
364
365        let secp = secp256k1::Secp256k1::new();
366        let message = "a different message from what was signed";
367        let msg_hash = super::signed_msg_hash(&message);
368
369        // Signature of msg = "rust-bitcoin MessageSignature test"
370        // Signed with pk "UuOGDsfLPr4HIMKQX0ipjJeRaj1geCq3yPUF2COP5ME="
371        let signature_base64 = "IAM2qX24tYx/bdBTIgVLhD8QEAjrPlJpmjB4nZHdRYGIBa4DmVulAcwjPnWe6Q5iEwXH6F0pUCJP/ZeHPWS1h1o=";
372        let pubkey_base64 = "A1FTfMEntPpAty3qkEo0q2Dc1FEycI10a3jmwEFy+Qr6";
373        let signature = super::MessageSignature::from_base64(signature_base64).expect("message signature");
374
375        let pubkey = PublicKey::from_slice(
376            &::base64::decode(&pubkey_base64).expect("base64 string")
377        ).expect("pubkey slice");
378
379        let p2pkh = ::Address::p2pkh(&pubkey, ::Network::Bitcoin);
380        assert_eq!(signature.is_signed_by_address(&secp, &p2pkh, msg_hash), Ok(false));
381    }
382}