ex3_crypto/verify/
btc.rs

1use crate::secp256k1::{Message, Signature};
2use crate::sha256;
3use crate::verify::MessageVerify;
4
5use std::io;
6use std::io::Write;
7
8pub struct BtcMessageVerify;
9
10impl MessageVerify for BtcMessageVerify {
11    fn verify<M, S, P>(message: M, signature: S, pub_key: Option<P>) -> crate::Result<Vec<u8>>
12    where
13        M: AsRef<[u8]>,
14        S: AsRef<[u8]>,
15        P: AsRef<[u8]>,
16    {
17        assert!(pub_key.is_none(), "btc message verify not need pub_key");
18
19        let message = message.as_ref();
20        let signature = signature.as_ref();
21
22        let signature = Signature::from_btc_signature(signature.into())?;
23        let msg_hash = signed_msg_hash(message);
24        let rec_id = signature.v() % 0x3;
25        let pub_key = signature.recover(&msg_hash, Some(rec_id))?;
26        pub_key.verify(&msg_hash, &signature)?;
27        Ok(pub_key.serialize(true))
28    }
29}
30
31fn signed_msg_hash(msg: &[u8]) -> Message {
32    let prefix = b"\x18Bitcoin Signed Message:\n";
33    let msg_len = VarInt(msg.len() as u64);
34    let mut prefixed_message = Vec::with_capacity(prefix.len() + msg_len.len() + msg.len());
35    prefixed_message.extend_from_slice(prefix);
36    msg_len
37        .consensus_encode(&mut prefixed_message)
38        .expect("encode message len don't error");
39    prefixed_message.extend_from_slice(msg);
40    let sha2_1: [u8; 32] = sha256(&prefixed_message);
41    let sha2_2: [u8; 32] = sha256(&sha2_1);
42    sha2_2
43}
44
45#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
46struct VarInt(pub u64);
47
48impl VarInt {
49    fn len(&self) -> usize {
50        match self.0 {
51            0..=0xFC => 1,
52            0xFD..=0xFFFF => 3,
53            0x10000..=0xFFFFFFFF => 5,
54            _ => 9,
55        }
56    }
57    fn consensus_encode<W: Write>(&self, w: &mut W) -> io::Result<()> {
58        match self.0 {
59            0..=0xFC => {
60                let len = self.0 as u8;
61                w.write_all(&[len])?;
62            }
63            0xFD..=0xFFFF => {
64                w.write_all(&[0xFD])?;
65                let len = self.0 as u16;
66                w.write_all(&len.to_le_bytes())?;
67            }
68            0x10000..=0xFFFFFFFF => {
69                w.write_all(&[0xFE])?;
70                let len = self.0 as u32;
71                w.write_all(&len.to_le_bytes())?;
72            }
73            _ => {
74                w.write_all(&[0xFF])?;
75                w.write_all(&self.0.to_le_bytes())?;
76            }
77        }
78        Ok(())
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use crate::secp256k1::tests::get_pub_key_from_wif;
86    use base64::Engine;
87
88    #[test]
89    fn should_match_pub_key_unisat() {
90        let signature_base64 = "G59hY32s8BK8OWvFr05+cm2zBCuoNml7PqACluyT5VjgR9BGuljW+HTPFg1I1DMZOQDX0BA4Z69OT8r9mgHcQ3E=";
91        let message_bytes = b"HelloWorld";
92        let private_wif = "L4DWptSwVurT18jMFvbbMQ2jMpf9uweHNefgzwe6tnzK5fkm1V5S";
93        let pub_key = get_pub_key_from_wif(private_wif);
94        let signature = base64::prelude::BASE64_STANDARD
95            .decode(signature_base64)
96            .unwrap();
97        let res = BtcMessageVerify::verify(message_bytes, &signature, None::<Vec<u8>>);
98        assert!(res.is_ok());
99        assert_eq!(res.unwrap(), pub_key.serialize(true));
100    }
101
102    #[test]
103    fn should_match_pub_key() {
104        let signature_base64 = "G2UXxeWn35RGEtcv8IEqSY6iNTc0m+LmLRfJLhzdxK6nM/5GkFMUoGEvgSLQ95HanQpAI0jA8IVAZs9oa6kaqEE=";
105        let message_bytes = b"HelloWorld";
106        let private_wif = "5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss";
107        let pub_key = get_pub_key_from_wif(private_wif);
108        let signature = base64::prelude::BASE64_STANDARD
109            .decode(signature_base64)
110            .unwrap();
111        let res = BtcMessageVerify::verify(message_bytes, &signature, None::<Vec<u8>>);
112        assert!(res.is_ok());
113        assert_eq!(res.unwrap(), pub_key.serialize(true));
114    }
115    #[test]
116    fn should_not_match_pub_key() {
117        let signature_base64 = "G4dtJhhw1PF7gEvdp7tKFN0kvg5mc9C9CgVYS7bnwzrF8cAwAju2aKnIF5r7st8DipS/1lTq/fdK7wb/AGs/MhI=";
118        let message_bytes = b"1";
119        let private_wif = "L4DWptSwVurT18jMFvbbMQ2jMpf9uweHNefgzwe6tnzK5fkm1V5S";
120        let pub_key = get_pub_key_from_wif(private_wif);
121        let signature = base64::prelude::BASE64_STANDARD
122            .decode(signature_base64)
123            .unwrap();
124        let res = BtcMessageVerify::verify(message_bytes, &signature, None::<Vec<u8>>);
125        assert!(res.is_err());
126    }
127}