bsv_wasm/keypair/
private_key.rs

1use crate::BSVErrors;
2use crate::ECIESCiphertext;
3use crate::ECDSA;
4use crate::ECIES;
5use crate::{Hash, PublicKey, SigningHash};
6use crate::{Signature, ToHex};
7use elliptic_curve::sec1::ToEncodedPoint;
8use k256::SecretKey;
9use rand_core::OsRng;
10#[cfg(target_arch = "wasm32")]
11use wasm_bindgen::prelude::*;
12#[cfg(target_arch = "wasm32")]
13use wasm_bindgen::throw_str;
14
15#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen)]
16#[derive(Debug, Clone)]
17pub struct PrivateKey {
18    pub(crate) secret_key: SecretKey,
19    pub(crate) is_pub_key_compressed: bool,
20}
21
22/**
23 * Internal Methods
24 */
25impl PrivateKey {
26    /**
27     * Standard ECDSA Message Signing
28     */
29    pub(crate) fn sign_message_impl(&self, msg: &[u8]) -> Result<Signature, BSVErrors> {
30        ECDSA::sign_with_deterministic_k_impl(self, msg, SigningHash::Sha256, false)
31    }
32
33    pub(crate) fn to_wif_impl(&self) -> Result<String, BSVErrors> {
34        // 1. Get Private Key hex
35        let priv_key_hex = self.to_hex();
36
37        // 2. Add 0x80 in front + 0x01 to end if compressed pub key
38
39        let padded_hex = match self.is_pub_key_compressed {
40            true => format!("80{}01", priv_key_hex),
41            false => format!("80{}", priv_key_hex),
42        };
43
44        // 3. SHA256d
45        let bytes = hex::decode(padded_hex.clone())?;
46
47        let shad_hex = Hash::sha_256d(&bytes).to_bytes();
48
49        // 4. Take first 4 bytes as checksum
50        let checksum = shad_hex.to_vec()[0..4].to_hex();
51
52        // 5. Add checksum to end of padded private key
53        let extended_key = format!("{}{}", padded_hex, checksum);
54
55        // 6 Base58 Result
56        let extended_key_bytes = hex::decode(extended_key)?;
57
58        Ok(bs58::encode(extended_key_bytes).into_string())
59    }
60
61    pub(crate) fn from_bytes_impl(bytes: &[u8]) -> Result<PrivateKey, BSVErrors> {
62        let secret_key = SecretKey::from_be_bytes(bytes)?;
63
64        Ok(PrivateKey {
65            secret_key,
66            is_pub_key_compressed: true,
67        })
68    }
69
70    pub(crate) fn from_hex_impl(hex_str: &str) -> Result<PrivateKey, BSVErrors> {
71        let bytes = hex::decode(hex_str)?;
72
73        Self::from_bytes_impl(&bytes)
74    }
75
76    pub(crate) fn from_wif_impl(wif_string: &str) -> Result<PrivateKey, BSVErrors> {
77        // 1. Decode from Base58
78        let wif_bytes = bs58::decode(wif_string).into_vec()?;
79        let wif_without_checksum = wif_bytes[0..wif_bytes.len() - 4].to_vec();
80
81        // 2. Check the Checksum
82        let checksum = wif_bytes[wif_bytes.len() - 4..].to_hex();
83        let check_hash = Hash::sha_256d(&wif_without_checksum).to_bytes();
84        let check_string = check_hash.to_vec()[0..4].to_hex();
85
86        if check_string.ne(&checksum) {
87            return Err(BSVErrors::FromWIF("Checksum does not match!".into()));
88        }
89
90        // Private Key is 32 bytes + prefix is 33 bytes, if 34 bytes and ends with 01, compressed is true
91        fn is_compressed(unchecksum: &[u8]) -> bool {
92            if unchecksum.len() < 34 {
93                return false;
94            }
95
96            match unchecksum.last() {
97                Some(last_byte) => last_byte.eq(&1),
98                None => false,
99            }
100        }
101
102        let is_compressed_pub_key = is_compressed(&wif_without_checksum);
103        // 3. Check if compressed public key, return private key string
104        let private_key_hex = match is_compressed_pub_key {
105            true => wif_without_checksum[1..wif_without_checksum.len() - 1].to_hex(),
106            false => wif_without_checksum[1..].to_hex(),
107        };
108
109        Ok(PrivateKey::from_hex_impl(&private_key_hex)?.compress_public_key(is_compressed_pub_key))
110    }
111
112    pub(crate) fn to_public_key_impl(&self) -> Result<PublicKey, BSVErrors> {
113        let pub_key = PublicKey::from_private_key_impl(self);
114
115        if !self.is_pub_key_compressed {
116            return pub_key.to_decompressed_impl();
117        }
118
119        Ok(pub_key)
120    }
121
122    /**
123     * Encrypt a message to the public key of this private key.
124     */
125    pub(crate) fn encrypt_message_impl(&self, message: &[u8]) -> Result<ECIESCiphertext, BSVErrors> {
126        ECIES::encrypt_impl(message, self, &self.to_public_key_impl()?, false)
127    }
128
129    /**
130     * Decrypt a message that was sent to the public key corresponding to this private key.
131     */
132    pub(crate) fn decrypt_message_impl(&self, ciphertext: &ECIESCiphertext, sender_pub_key: &PublicKey) -> Result<Vec<u8>, BSVErrors> {
133        ECIES::decrypt_impl(ciphertext, self, sender_pub_key)
134    }
135}
136
137#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen)]
138impl PrivateKey {
139    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = toBytes))]
140    pub fn to_bytes(&self) -> Vec<u8> {
141        self.secret_key.to_be_bytes().to_vec()
142    }
143
144    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = toHex))]
145    pub fn to_hex(&self) -> String {
146        let secret_key_bytes = self.to_bytes();
147        hex::encode(secret_key_bytes)
148    }
149
150    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = fromRandom))]
151    pub fn from_random() -> PrivateKey {
152        let secret_key = k256::SecretKey::random(&mut OsRng);
153
154        PrivateKey {
155            secret_key,
156            is_pub_key_compressed: true,
157        }
158    }
159
160    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = getPoint))]
161    /**
162     * Finds the Public Key Point.
163     */
164    pub fn get_point(&self) -> Vec<u8> {
165        self.secret_key.public_key().as_affine().to_encoded_point(self.is_pub_key_compressed).as_bytes().into()
166    }
167
168    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = compressPublicKey))]
169    pub fn compress_public_key(&self, should_compress: bool) -> PrivateKey {
170        let mut priv_key = self.clone();
171        priv_key.is_pub_key_compressed = should_compress;
172        priv_key
173    }
174}
175
176/**
177 * WASM Exported Methods
178 */
179#[cfg(target_arch = "wasm32")]
180#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen)]
181impl PrivateKey {
182    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = fromWIF))]
183    pub fn from_wif(wif_string: &str) -> Result<PrivateKey, JsValue> {
184        match PrivateKey::from_wif_impl(wif_string) {
185            Ok(v) => Ok(v),
186            Err(e) => Err(JsValue::from_str(&e.to_string())),
187        }
188    }
189
190    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = fromHex))]
191    pub fn from_hex(hex_str: &str) -> Result<PrivateKey, JsValue> {
192        match PrivateKey::from_hex_impl(hex_str) {
193            Ok(v) => Ok(v),
194            Err(e) => Err(JsValue::from_str(&e.to_string())),
195        }
196    }
197
198    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = signMessage))]
199    /**
200     * Standard ECDSA Message Signing using SHA256 as the digestg
201     */
202    pub fn sign_message(&self, msg: &[u8]) -> Result<Signature, JsValue> {
203        match PrivateKey::sign_message_impl(&self, msg) {
204            Ok(v) => Ok(v),
205            Err(e) => Err(JsValue::from_str(&e.to_string())),
206        }
207    }
208
209    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = toWIF))]
210    pub fn to_wif(&self) -> Result<String, JsValue> {
211        match PrivateKey::to_wif_impl(&self) {
212            Ok(v) => Ok(v),
213            Err(e) => Err(JsValue::from_str(&e.to_string())),
214        }
215    }
216
217    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = fromBytes))]
218    pub fn from_bytes(bytes: &[u8]) -> Result<PrivateKey, JsValue> {
219        match Self::from_bytes_impl(bytes) {
220            Ok(v) => Ok(v),
221            Err(e) => Err(JsValue::from_str(&e.to_string())),
222        }
223    }
224
225    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = toPublicKey))]
226    pub fn to_public_key(&self) -> Result<PublicKey, JsValue> {
227        match self.to_public_key_impl() {
228            Ok(v) => Ok(v),
229            Err(e) => Err(JsValue::from_str(&e.to_string())),
230        }
231    }
232
233    /**
234     * Encrypt a message to the public key of this private key.
235     */
236    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = encryptMessage))]
237    pub fn encrypt_message(&self, message: &[u8]) -> Result<ECIESCiphertext, JsValue> {
238        match self.encrypt_message_impl(message) {
239            Ok(v) => Ok(v),
240            Err(e) => Err(JsValue::from_str(&e.to_string())),
241        }
242    }
243
244    /**
245     * Decrypt a message that was sent to the public key corresponding to this private key.
246     */
247    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-keypair"), wasm_bindgen(js_name = decryptMessage))]
248    pub fn decrypt_message(&self, ciphertext: &ECIESCiphertext, sender_pub_key: &PublicKey) -> Result<Vec<u8>, JsValue> {
249        match self.decrypt_message_impl(ciphertext, sender_pub_key) {
250            Ok(v) => Ok(v),
251            Err(e) => Err(JsValue::from_str(&e.to_string())),
252        }
253    }
254}
255
256/**
257 * Native Exported Methods
258 */
259#[cfg(not(target_arch = "wasm32"))]
260impl PrivateKey {
261    pub fn to_wif(&self) -> Result<String, BSVErrors> {
262        PrivateKey::to_wif_impl(self)
263    }
264
265    pub fn from_wif(wif_string: &str) -> Result<PrivateKey, BSVErrors> {
266        PrivateKey::from_wif_impl(wif_string)
267    }
268
269    pub fn from_hex(hex_str: &str) -> Result<PrivateKey, BSVErrors> {
270        PrivateKey::from_hex_impl(hex_str)
271    }
272
273    /**
274     * Standard ECDSA Message Signing using SHA256 as the digestg
275     */
276    pub fn sign_message(&self, msg: &[u8]) -> Result<Signature, BSVErrors> {
277        PrivateKey::sign_message_impl(self, msg)
278    }
279
280    pub fn from_bytes(bytes: &[u8]) -> Result<PrivateKey, BSVErrors> {
281        Self::from_bytes_impl(bytes)
282    }
283
284    pub fn to_public_key(&self) -> Result<PublicKey, BSVErrors> {
285        self.to_public_key_impl()
286    }
287
288    /**
289     * Encrypt a message to the public key of this private key.
290     */
291    pub fn encrypt_message(&self, message: &[u8]) -> Result<ECIESCiphertext, BSVErrors> {
292        self.encrypt_message_impl(message)
293    }
294
295    /**
296     * Decrypt a message that was sent to the public key corresponding to this private key.
297     */
298    pub fn decrypt_message(&self, ciphertext: &ECIESCiphertext, sender_pub_key: &PublicKey) -> Result<Vec<u8>, BSVErrors> {
299        self.decrypt_message_impl(ciphertext, sender_pub_key)
300    }
301}