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 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 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#[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#[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}