1use hashes::{sha256d, Hash, HashEngine};
10
11use crate::consensus::{encode, Encodable};
12
13#[rustfmt::skip]
14#[doc(inline)]
15#[cfg(feature = "secp-recovery")]
16pub use self::message_signing::{MessageSignature, MessageSignatureError};
17
18pub const BITCOIN_SIGNED_MSG_PREFIX: &[u8] = b"\x18Bitcoin Signed Message:\n";
20
21#[cfg(feature = "secp-recovery")]
22mod message_signing {
23 use core::fmt;
24
25 use hashes::{sha256d, Hash};
26 use internals::write_err;
27 use secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
28
29 use crate::address::{Address, AddressType};
30 use crate::crypto::key::PublicKey;
31
32 #[derive(Debug, Clone, PartialEq, Eq)]
34 #[non_exhaustive]
35 pub enum MessageSignatureError {
36 InvalidLength,
38 InvalidEncoding(secp256k1::Error),
40 InvalidBase64,
42 UnsupportedAddressType(AddressType),
44 }
45
46 internals::impl_from_infallible!(MessageSignatureError);
47
48 impl fmt::Display for MessageSignatureError {
49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 use MessageSignatureError::*;
51
52 match *self {
53 InvalidLength => write!(f, "length not 65 bytes"),
54 InvalidEncoding(ref e) => write_err!(f, "invalid encoding"; e),
55 InvalidBase64 => write!(f, "invalid base64"),
56 UnsupportedAddressType(ref address_type) =>
57 write!(f, "unsupported address type: {}", address_type),
58 }
59 }
60 }
61
62 #[cfg(feature = "std")]
63 impl std::error::Error for MessageSignatureError {
64 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
65 use MessageSignatureError::*;
66
67 match *self {
68 InvalidEncoding(ref e) => Some(e),
69 InvalidLength | InvalidBase64 | UnsupportedAddressType(_) => None,
70 }
71 }
72 }
73
74 impl From<secp256k1::Error> for MessageSignatureError {
75 fn from(e: secp256k1::Error) -> MessageSignatureError {
76 MessageSignatureError::InvalidEncoding(e)
77 }
78 }
79
80 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
86 pub struct MessageSignature {
87 pub signature: RecoverableSignature,
89 pub compressed: bool,
91 }
92
93 impl MessageSignature {
94 pub fn new(signature: RecoverableSignature, compressed: bool) -> MessageSignature {
96 MessageSignature { signature, compressed }
97 }
98
99 pub fn serialize(&self) -> [u8; 65] {
101 let (recid, raw) = self.signature.serialize_compact();
102 let mut serialized = [0u8; 65];
103 serialized[0] = 27;
104 serialized[0] += recid.to_i32() as u8;
105 if self.compressed {
106 serialized[0] += 4;
107 }
108 serialized[1..].copy_from_slice(&raw[..]);
109 serialized
110 }
111
112 pub fn from_slice(bytes: &[u8]) -> Result<MessageSignature, MessageSignatureError> {
114 if bytes.len() != 65 {
115 return Err(MessageSignatureError::InvalidLength);
116 }
117 if bytes[0] < 27 {
119 return Err(MessageSignatureError::InvalidEncoding(
120 secp256k1::Error::InvalidRecoveryId,
121 ));
122 };
123 let recid = RecoveryId::from_i32(((bytes[0] - 27) & 0x03) as i32)?;
124 Ok(MessageSignature {
125 signature: RecoverableSignature::from_compact(&bytes[1..], recid)?,
126 compressed: ((bytes[0] - 27) & 0x04) != 0,
127 })
128 }
129
130 pub fn recover_pubkey<C: secp256k1::Verification>(
134 &self,
135 secp_ctx: &secp256k1::Secp256k1<C>,
136 msg_hash: sha256d::Hash,
137 ) -> Result<PublicKey, MessageSignatureError> {
138 let msg = secp256k1::Message::from_digest(msg_hash.to_byte_array());
139 let pubkey = secp_ctx.recover_ecdsa(&msg, &self.signature)?;
140 Ok(PublicKey { inner: pubkey, compressed: self.compressed })
141 }
142
143 pub fn is_signed_by_address<C: secp256k1::Verification>(
147 &self,
148 secp_ctx: &secp256k1::Secp256k1<C>,
149 address: &Address,
150 msg_hash: sha256d::Hash,
151 ) -> Result<bool, MessageSignatureError> {
152 match address.address_type() {
153 Some(AddressType::P2pkh) => {
154 let pubkey = self.recover_pubkey(secp_ctx, msg_hash)?;
155 Ok(address.pubkey_hash() == Some(pubkey.pubkey_hash()))
156 }
157 Some(address_type) =>
158 Err(MessageSignatureError::UnsupportedAddressType(address_type)),
159 None => Ok(false),
160 }
161 }
162 }
163
164 #[cfg(feature = "base64")]
165 mod base64_impls {
166 use base64::prelude::{Engine as _, BASE64_STANDARD};
167
168 use super::*;
169 use crate::prelude::*;
170
171 impl MessageSignature {
172 pub fn from_base64(s: &str) -> Result<MessageSignature, MessageSignatureError> {
174 let bytes =
175 BASE64_STANDARD.decode(s).map_err(|_| MessageSignatureError::InvalidBase64)?;
176 MessageSignature::from_slice(&bytes)
177 }
178
179 pub fn to_base64(self) -> String { BASE64_STANDARD.encode(self.serialize()) }
181 }
182
183 impl fmt::Display for MessageSignature {
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 let bytes = self.serialize();
186 write!(f, "{}", base64::display::Base64Display::new(&bytes, &BASE64_STANDARD))
188 }
189 }
190
191 impl core::str::FromStr for MessageSignature {
192 type Err = MessageSignatureError;
193 fn from_str(s: &str) -> Result<MessageSignature, MessageSignatureError> {
194 MessageSignature::from_base64(s)
195 }
196 }
197 }
198}
199
200pub fn signed_msg_hash(msg: &str) -> sha256d::Hash {
202 let mut engine = sha256d::Hash::engine();
203 engine.input(BITCOIN_SIGNED_MSG_PREFIX);
204 let msg_len = encode::VarInt::from(msg.len());
205 msg_len.consensus_encode(&mut engine).expect("engines don't error");
206 engine.input(msg.as_bytes());
207 sha256d::Hash::from_engine(engine)
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_signed_msg_hash() {
216 let hash = signed_msg_hash("test");
217 assert_eq!(
218 hash.to_string(),
219 "a6f87fe6d58a032c320ff8d1541656f0282c2c7bfcc69d61af4c8e8ed528e49c"
220 );
221 }
222
223 #[test]
224 #[cfg(all(feature = "secp-recovery", feature = "base64", feature = "rand-std"))]
225 fn test_message_signature() {
226 use core::str::FromStr;
227
228 use secp256k1;
229
230 use crate::{Address, AddressType, Network, NetworkKind};
231
232 let secp = secp256k1::Secp256k1::new();
233 let message = "rust-bitcoin MessageSignature test";
234 let msg_hash = super::signed_msg_hash(message);
235 let msg = secp256k1::Message::from_digest(msg_hash.to_byte_array());
236 let privkey = secp256k1::SecretKey::new(&mut secp256k1::rand::thread_rng());
237 let secp_sig = secp.sign_ecdsa_recoverable(&msg, &privkey);
238 let signature = super::MessageSignature { signature: secp_sig, compressed: true };
239
240 assert_eq!(signature.to_base64(), signature.to_string());
241 let signature2 = super::MessageSignature::from_str(&signature.to_string()).unwrap();
242 let pubkey = signature2
243 .recover_pubkey(&secp, msg_hash)
244 .unwrap()
245 .try_into()
246 .expect("compressed was set to true");
247
248 let p2pkh = Address::p2pkh(pubkey, NetworkKind::Main);
249 assert_eq!(signature2.is_signed_by_address(&secp, &p2pkh, msg_hash), Ok(true));
250 let p2wpkh = Address::p2wpkh(&pubkey, Network::Bitcoin);
251 assert_eq!(
252 signature2.is_signed_by_address(&secp, &p2wpkh, msg_hash),
253 Err(MessageSignatureError::UnsupportedAddressType(AddressType::P2wpkh))
254 );
255 let p2shwpkh = Address::p2shwpkh(&pubkey, NetworkKind::Main);
256 assert_eq!(
257 signature2.is_signed_by_address(&secp, &p2shwpkh, msg_hash),
258 Err(MessageSignatureError::UnsupportedAddressType(AddressType::P2sh))
259 );
260 let p2pkh = Address::p2pkh(pubkey, Network::Bitcoin);
261 assert_eq!(signature2.is_signed_by_address(&secp, &p2pkh, msg_hash), Ok(true));
262
263 assert_eq!(pubkey.0, secp256k1::PublicKey::from_secret_key(&secp, &privkey));
264 }
265
266 #[test]
267 #[cfg(all(feature = "secp-recovery", feature = "base64"))]
268 fn test_incorrect_message_signature() {
269 use base64::prelude::{Engine as _, BASE64_STANDARD};
270 use secp256k1;
271
272 use crate::crypto::key::PublicKey;
273 use crate::{Address, NetworkKind};
274
275 let secp = secp256k1::Secp256k1::new();
276 let message = "a different message from what was signed";
277 let msg_hash = super::signed_msg_hash(message);
278
279 let signature_base64 = "IAM2qX24tYx/bdBTIgVLhD8QEAjrPlJpmjB4nZHdRYGIBa4DmVulAcwjPnWe6Q5iEwXH6F0pUCJP/ZeHPWS1h1o=";
282 let pubkey_base64 = "A1FTfMEntPpAty3qkEo0q2Dc1FEycI10a3jmwEFy+Qr6";
283 let signature =
284 super::MessageSignature::from_base64(signature_base64).expect("message signature");
285
286 let pubkey =
287 PublicKey::from_slice(&BASE64_STANDARD.decode(pubkey_base64).expect("base64 string"))
288 .expect("pubkey slice");
289
290 let p2pkh = Address::p2pkh(pubkey, NetworkKind::Main);
291 assert_eq!(signature.is_signed_by_address(&secp, &p2pkh, msg_hash), Ok(false));
292 }
293}