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#[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 pub(crate) fn sign_impl(priv_key: &PrivateKey, message: &[u8]) -> Result<Signature, BSVErrors> {
36 let magic_message = BSM::prepend_magic_bytes(message)?;
37 ECDSA::sign_with_deterministic_k_impl(priv_key, &magic_message, SigningHash::Sha256d, false)
39 }
40
41 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 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 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 #[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}