bsv_wasm/bsm/
mod.rs

1use crate::BSVErrors;
2use crate::VarIntWriter;
3use std::io::Write;
4
5use crate::{P2PKHAddress, PrivateKey, Signature, SigningHash, ECDSA};
6#[cfg(target_arch = "wasm32")]
7use wasm_bindgen::prelude::*;
8#[cfg(target_arch = "wasm32")]
9use wasm_bindgen::{throw_str, JsValue};
10
11/**
12 * Bitcoin Signed Message
13 */
14#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-bsm"), wasm_bindgen)]
15pub struct BSM {}
16
17const MAGIC_BYTES: &[u8] = b"Bitcoin Signed Message:\n";
18
19impl BSM {
20    fn prepend_magic_bytes(msg: &[u8]) -> Result<Vec<u8>, BSVErrors> {
21        let mut buffer: Vec<u8> = vec![];
22
23        buffer.write_varint(MAGIC_BYTES.len() as u64)?;
24        buffer.write_all(MAGIC_BYTES)?;
25        buffer.write_varint(msg.len() as u64)?;
26        buffer.write_all(msg)?;
27
28        Ok(buffer)
29    }
30
31    /**
32     * Sign a message with the intention of verifying with this same Address.
33     * Used when using Bitcoin Signed Messages ()
34     */
35    pub(crate) fn sign_impl(priv_key: &PrivateKey, message: &[u8]) -> Result<Signature, BSVErrors> {
36        let magic_message = BSM::prepend_magic_bytes(message)?;
37        // let magic_message = message;
38        ECDSA::sign_with_deterministic_k_impl(priv_key, &magic_message, SigningHash::Sha256d, false)
39    }
40
41    /**
42     * Sign a Bitcoin Signed Message with a specific K value. I hope you know what you're doing!
43     */
44    pub(crate) fn sign_with_k_impl(priv_key: &PrivateKey, ephemeral_key: &PrivateKey, message: &[u8]) -> Result<Signature, BSVErrors> {
45        let magic_message = BSM::prepend_magic_bytes(message)?;
46        // let magic_message = message;
47        ECDSA::sign_with_k_impl(priv_key, ephemeral_key, &magic_message, SigningHash::Sha256d)
48    }
49
50    pub(crate) fn verify_message_impl(message: &[u8], signature: &Signature, address: &P2PKHAddress) -> Result<bool, BSVErrors> {
51        let magic_message = BSM::prepend_magic_bytes(message)?;
52        // let magic_message = message;
53
54        let public_key = signature.get_public_key(&magic_message, SigningHash::Sha256d)?;
55        let verify_p2pkh = P2PKHAddress::from_pubkey_impl(&public_key)?;
56
57        let verify_address = verify_p2pkh.to_address_string_impl()?;
58        let address_string = address.to_address_string_impl()?;
59        if verify_address != address_string {
60            return Err(BSVErrors::MessageVerification(format!(
61                "Provided address ({}) does not match signature address ({})",
62                address_string, verify_address
63            )));
64        }
65        ECDSA::verify_digest_impl(&magic_message, &public_key, signature, SigningHash::Sha256d)?;
66        Ok(true)
67    }
68}
69
70#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-bsm"), wasm_bindgen)]
71impl BSM {
72    /**
73     * Sign a message with the intention of verifying with this same Address.
74     * Used when using Bitcoin Signed Messages
75     *
76     * Returns boolean
77     */
78    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-bsm"), wasm_bindgen(js_name = isValidMessage))]
79    pub fn is_valid_message(message: &[u8], signature: &Signature, address: &P2PKHAddress) -> bool {
80        BSM::verify_message_impl(message, signature, address).is_ok()
81    }
82}
83
84#[cfg(target_arch = "wasm32")]
85#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-bsm"), wasm_bindgen)]
86impl BSM {
87    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-bsm"), wasm_bindgen(js_name = verifyMessage))]
88    pub fn verify_message(message: &[u8], signature: &Signature, address: &P2PKHAddress) -> Result<bool, JsValue> {
89        match BSM::verify_message_impl(message, signature, address) {
90            Ok(v) => Ok(v),
91            Err(e) => Err(JsValue::from_str(&e.to_string())),
92        }
93    }
94
95    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-bsm"), wasm_bindgen(js_name = signMessage))]
96    pub fn sign_message(priv_key: &PrivateKey, message: &[u8]) -> Result<Signature, JsValue> {
97        match BSM::sign_impl(priv_key, message) {
98            Ok(v) => Ok(v),
99            Err(e) => Err(JsValue::from_str(&e.to_string())),
100        }
101    }
102
103    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-bsm"), wasm_bindgen(js_name = signMessageWithK))]
104    pub fn sign_message_with_k(priv_key: &PrivateKey, ephemeral_key: &PrivateKey, message: &[u8]) -> Result<Signature, JsValue> {
105        match BSM::sign_with_k_impl(priv_key, ephemeral_key, message) {
106            Ok(v) => Ok(v),
107            Err(e) => throw_str(&e.to_string()),
108        }
109    }
110}
111
112#[cfg(not(target_arch = "wasm32"))]
113impl BSM {
114    pub fn verify_message(message: &[u8], signature: &Signature, address: &P2PKHAddress) -> Result<bool, BSVErrors> {
115        BSM::verify_message_impl(message, signature, address)
116    }
117
118    pub fn sign_message(priv_key: &PrivateKey, message: &[u8]) -> Result<Signature, BSVErrors> {
119        BSM::sign_impl(priv_key, message)
120    }
121
122    pub fn sign_message_with_k(priv_key: &PrivateKey, ephemeral_key: &PrivateKey, message: &[u8]) -> Result<Signature, BSVErrors> {
123        BSM::sign_with_k_impl(priv_key, ephemeral_key, message)
124    }
125}