bsv_wasm/keypair/
public_key.rs

1use crate::{BSVErrors, ECIESCiphertext, P2PKHAddress, Signature, SigningHash, ECDSA, ECIES};
2use elliptic_curve::{sec1::*, subtle::Choice};
3use k256::{AffinePoint, Secp256k1};
4#[cfg(target_arch = "wasm32")]
5use wasm_bindgen::{prelude::*, throw_str};
6
7use crate::PrivateKey;
8
9#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen)]
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct PublicKey {
12    point: Vec<u8>,
13    is_compressed: bool,
14}
15
16impl PublicKey {
17    pub(crate) fn from_private_key_impl(priv_key: &PrivateKey) -> PublicKey {
18        PublicKey {
19            point: priv_key.get_point(),
20            is_compressed: priv_key.is_pub_key_compressed,
21        }
22    }
23
24    pub(crate) fn to_hex_impl(&self) -> Result<String, BSVErrors> {
25        let bytes = self.to_bytes_impl()?;
26        Ok(hex::encode(bytes))
27    }
28
29    pub(crate) fn to_bytes_impl(&self) -> Result<Vec<u8>, BSVErrors> {
30        Ok(self.point.clone())
31    }
32
33    pub(crate) fn from_bytes_impl(bytes: &[u8]) -> Result<PublicKey, BSVErrors> {
34        let point = EncodedPoint::<Secp256k1>::from_bytes(bytes).map_err(|e| BSVErrors::PublicKeyError(e.to_string()))?;
35        Ok(PublicKey::from_encoded_point(&point))
36    }
37
38    fn from_encoded_point(point: &EncodedPoint<Secp256k1>) -> PublicKey {
39        PublicKey {
40            point: point.as_bytes().to_vec(),
41            is_compressed: point.is_compressed(),
42        }
43    }
44
45    pub(crate) fn to_decompressed_impl(&self) -> Result<PublicKey, BSVErrors> {
46        use elliptic_curve::DecompressPoint;
47
48        let point = EncodedPoint::<Secp256k1>::from_bytes(&self.point).unwrap();
49
50        let decompressed_point: EncodedPoint<Secp256k1> = match point.coordinates() {
51            Coordinates::Compressed { x, y_is_odd } => AffinePoint::decompress(x, Choice::from(y_is_odd as u8)).map(|s| s.to_encoded_point(false)).into(),
52            Coordinates::Compact { .. } | Coordinates::Identity => None,
53            Coordinates::Uncompressed { .. } => Some(point),
54        }
55        .unwrap();
56
57        Ok(PublicKey::from_encoded_point(&decompressed_point))
58    }
59
60    pub(crate) fn to_compressed_impl(&self) -> Result<PublicKey, BSVErrors> {
61        let point = EncodedPoint::<Secp256k1>::from_bytes(&self.point).map_err(|e| BSVErrors::PublicKeyError(e.to_string()))?;
62        Ok(PublicKey::from_encoded_point(&point.compress()))
63    }
64
65    pub(crate) fn from_hex_impl(hex_str: &str) -> Result<PublicKey, BSVErrors> {
66        let point_bytes = hex::decode(hex_str)?;
67        PublicKey::from_bytes_impl(&point_bytes)
68    }
69
70    /**
71     * Standard ECDSA Message Verification
72     */
73    pub(crate) fn verify_message_impl(&self, message: &[u8], signature: &Signature) -> Result<bool, BSVErrors> {
74        ECDSA::verify_digest_impl(message, self, signature, SigningHash::Sha256)
75    }
76
77    pub(crate) fn to_p2pkh_address_impl(&self) -> Result<P2PKHAddress, BSVErrors> {
78        P2PKHAddress::from_pubkey_impl(self)
79    }
80
81    /**
82     * Encrypt a message to be sent to this public key with the provided private key.
83     */
84    pub(crate) fn encrypt_message_impl(&self, message: &[u8], sender_private_key: &PrivateKey) -> Result<ECIESCiphertext, BSVErrors> {
85        ECIES::encrypt_impl(message, sender_private_key, self, false)
86    }
87}
88
89#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen)]
90impl PublicKey {
91    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = isValidMessage))]
92    pub fn is_valid_message(&self, message: &[u8], signature: &Signature) -> bool {
93        self.verify_message_impl(message, signature).is_ok()
94    }
95
96    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = isCompressed))]
97    pub fn is_compressed(&self) -> bool {
98        self.is_compressed
99    }
100}
101
102/**
103 * WASM Exported Methods
104 */
105#[cfg(target_arch = "wasm32")]
106#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen)]
107impl PublicKey {
108    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = fromHex))]
109    pub fn from_hex(hex_str: &str) -> Result<PublicKey, JsValue> {
110        match PublicKey::from_hex_impl(hex_str) {
111            Ok(v) => Ok(v),
112            Err(e) => Err(JsValue::from_str(&e.to_string())),
113        }
114    }
115
116    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = fromBytes))]
117    pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey, JsValue> {
118        match PublicKey::from_bytes_impl(bytes) {
119            Ok(v) => Ok(v),
120            Err(e) => Err(JsValue::from_str(&e.to_string())),
121        }
122    }
123
124    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = toBytes))]
125    pub fn to_bytes(&self) -> Result<Vec<u8>, JsValue> {
126        match PublicKey::to_bytes_impl(&self) {
127            Ok(v) => Ok(v),
128            Err(e) => Err(JsValue::from_str(&e.to_string())),
129        }
130    }
131
132    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = toHex))]
133    pub fn to_hex(&self) -> Result<String, JsValue> {
134        match PublicKey::to_hex_impl(&self) {
135            Ok(v) => Ok(v),
136            Err(e) => Err(JsValue::from_str(&e.to_string())),
137        }
138    }
139
140    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = fromPrivateKey))]
141    pub fn from_private_key(priv_key: &PrivateKey) -> PublicKey {
142        PublicKey::from_private_key_impl(priv_key)
143    }
144
145    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = verifyMessage))]
146    pub fn verify_message(&self, message: &[u8], signature: &Signature) -> Result<bool, JsValue> {
147        match self.verify_message_impl(message, signature) {
148            Ok(v) => Ok(v),
149            Err(e) => Err(JsValue::from_str(&e.to_string())),
150        }
151    }
152
153    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = toAddress))]
154    pub fn to_p2pkh_address(&self) -> Result<P2PKHAddress, JsValue> {
155        match self.to_p2pkh_address_impl() {
156            Ok(v) => Ok(v),
157            Err(e) => Err(JsValue::from_str(&e.to_string())),
158        }
159    }
160
161    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = toCompressed))]
162    pub fn to_compressed(&self) -> Result<PublicKey, JsValue> {
163        match self.to_compressed_impl() {
164            Ok(v) => Ok(v),
165            Err(e) => Err(JsValue::from_str(&e.to_string())),
166        }
167    }
168
169    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = toDecompressed))]
170    pub fn to_decompressed(&self) -> Result<PublicKey, JsValue> {
171        match self.to_decompressed_impl() {
172            Ok(v) => Ok(v),
173            Err(e) => Err(JsValue::from_str(&e.to_string())),
174        }
175    }
176
177    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = encryptMessage))]
178    pub fn encrypt_message(&self, message: &[u8], sender_private_key: &PrivateKey) -> Result<ECIESCiphertext, JsValue> {
179        match self.encrypt_message_impl(message, sender_private_key) {
180            Ok(v) => Ok(v),
181            Err(e) => Err(JsValue::from_str(&e.to_string())),
182        }
183    }
184}
185
186/**
187 * Native Exported Methods
188 */
189#[cfg(not(target_arch = "wasm32"))]
190impl PublicKey {
191    pub fn from_hex(hex_str: &str) -> Result<PublicKey, BSVErrors> {
192        PublicKey::from_hex_impl(hex_str)
193    }
194
195    pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey, BSVErrors> {
196        PublicKey::from_bytes_impl(bytes)
197    }
198
199    pub fn to_bytes(&self) -> Result<Vec<u8>, BSVErrors> {
200        PublicKey::to_bytes_impl(self)
201    }
202
203    pub fn to_hex(&self) -> Result<String, BSVErrors> {
204        PublicKey::to_hex_impl(self)
205    }
206
207    pub fn from_private_key(priv_key: &PrivateKey) -> PublicKey {
208        PublicKey::from_private_key_impl(priv_key)
209    }
210
211    pub fn verify_message(&self, message: &[u8], signature: &Signature) -> Result<bool, BSVErrors> {
212        self.verify_message_impl(message, signature)
213    }
214
215    pub fn to_p2pkh_address(&self) -> Result<P2PKHAddress, BSVErrors> {
216        self.to_p2pkh_address_impl()
217    }
218
219    pub fn to_compressed(&self) -> Result<PublicKey, BSVErrors> {
220        self.to_compressed_impl()
221    }
222
223    pub fn to_decompressed(&self) -> Result<PublicKey, BSVErrors> {
224        self.to_decompressed_impl()
225    }
226
227    pub fn encrypt_message(&self, message: &[u8], sender_private_key: &PrivateKey) -> Result<ECIESCiphertext, BSVErrors> {
228        self.encrypt_message_impl(message, sender_private_key)
229    }
230}