bsv_wasm/ecies/
mod.rs

1use crate::{BSVErrors, Hash, PrivateKey, PublicKey, AES};
2use elliptic_curve::sec1::ToEncodedPoint;
3use k256::PublicKey as K256PublicKey;
4#[cfg(target_arch = "wasm32")]
5use wasm_bindgen::prelude::*;
6#[cfg(target_arch = "wasm32")]
7use wasm_bindgen::{throw_str, JsValue};
8
9pub mod ecies_ciphertext;
10pub use ecies_ciphertext::*;
11
12/**
13 * Electrum compatible ECIES implementation.
14 * Comparable to Ecies.electrumEncrypt in BSV.JS
15 */
16#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen)]
17#[derive(Clone)]
18pub struct ECIES {}
19
20#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen)]
21#[derive(Clone)]
22pub struct CipherKeys {
23    pub(crate) iv: Vec<u8>,
24    pub(crate) ke: Vec<u8>,
25    pub(crate) km: Vec<u8>,
26}
27
28#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen)]
29impl CipherKeys {
30    pub fn get_iv(&self) -> Vec<u8> {
31        self.iv.clone()
32    }
33
34    pub fn get_ke(&self) -> Vec<u8> {
35        self.ke.clone()
36    }
37
38    pub fn get_km(&self) -> Vec<u8> {
39        self.km.clone()
40    }
41}
42
43impl ECIES {
44    pub(crate) fn encrypt_impl(message: &[u8], private_key: &PrivateKey, recipient_pub_key: &PublicKey, exclude_pub_key: bool) -> Result<ECIESCiphertext, BSVErrors> {
45        let cipher = ECIES::derive_cipher_keys_impl(private_key, recipient_pub_key)?;
46        let cipher_text = AES::encrypt_impl(&cipher.ke, &cipher.iv, message, crate::AESAlgorithms::AES128_CBC)?;
47
48        let mut buffer: Vec<u8> = Vec::new();
49        buffer.extend_from_slice(b"BIE1");
50
51        let r_buf = match exclude_pub_key {
52            true => None,
53            false => {
54                let pub_key = private_key.to_public_key_impl()?.to_compressed_impl()?.to_bytes_impl()?;
55                buffer.extend_from_slice(&pub_key);
56                Some(pub_key)
57            }
58        };
59        buffer.extend_from_slice(&cipher_text);
60
61        let hmac = Hash::sha_256_hmac(&buffer, &cipher.km).to_bytes();
62
63        Ok(ECIESCiphertext {
64            ciphertext_bytes: cipher_text,
65            public_key_bytes: r_buf,
66            hmac_bytes: hmac,
67            keys: Some(cipher),
68        })
69    }
70
71    /**
72     * Encrypt with a randomly generate private key.
73     * This is intended to be used if you want to anonymously send a party an encrypted message.
74     */
75    pub(crate) fn encrypt_with_ephemeral_private_key_impl(message: &[u8], recipient_pub_key: &PublicKey) -> Result<ECIESCiphertext, BSVErrors> {
76        let private_key = PrivateKey::from_random();
77        ECIES::encrypt_impl(message, &private_key, recipient_pub_key, false)
78    }
79
80    pub(crate) fn decrypt_impl(ciphertext: &ECIESCiphertext, recipient_priv_key: &PrivateKey, sender_pub_key: &PublicKey) -> Result<Vec<u8>, BSVErrors> {
81        let cipher_keys = ECIES::derive_cipher_keys_impl(recipient_priv_key, sender_pub_key)?;
82
83        let hmac = &ciphertext.hmac_bytes;
84
85        let mut preimage = b"BIE1".to_vec();
86        if let Some(pk) = &ciphertext.public_key_bytes {
87            preimage.extend_from_slice(pk);
88        }
89        preimage.extend_from_slice(&ciphertext.ciphertext_bytes);
90
91        let verify_hmac = Hash::sha_256_hmac(&preimage, &cipher_keys.km);
92
93        if hmac != &verify_hmac.to_bytes() {
94            return Err(BSVErrors::ECIESError("Invalid Checksum".into()));
95        }
96
97        let plain_text = AES::decrypt_impl(&cipher_keys.ke, &cipher_keys.iv, &ciphertext.ciphertext_bytes, crate::AESAlgorithms::AES128_CBC)?;
98        Ok(plain_text)
99    }
100
101    pub(crate) fn derive_cipher_keys_impl(priv_key: &PrivateKey, pub_key: &PublicKey) -> Result<CipherKeys, BSVErrors> {
102        let private_scalar = *priv_key.secret_key.to_nonzero_scalar();
103        let pub_key_point = K256PublicKey::from_sec1_bytes(&pub_key.to_bytes_impl()?)?.to_projective();
104
105        let shared_point = pub_key_point * private_scalar;
106        let shared_pub = K256PublicKey::from_affine(shared_point.to_affine())?;
107
108        let shared_mod_point = shared_pub.to_encoded_point(true);
109        let hash = Hash::sha_512(shared_mod_point.as_bytes()).to_bytes();
110
111        Ok(CipherKeys {
112            iv: hash[0..16].into(),
113            ke: hash[16..32].into(),
114            km: hash[32..64].into(),
115        })
116    }
117}
118
119#[cfg(target_arch = "wasm32")]
120#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen)]
121impl ECIES {
122    pub fn encrypt(message: &[u8], sender_priv_key: &PrivateKey, recipient_pub_key: &PublicKey, exclude_pub_key: bool) -> Result<ECIESCiphertext, JsValue> {
123        match ECIES::encrypt_impl(message, sender_priv_key, recipient_pub_key, exclude_pub_key) {
124            Ok(v) => Ok(v),
125            Err(e) => Err(JsValue::from_str(&e.to_string())),
126        }
127    }
128
129    /**
130     * Encrypt with a randomly generate private key.
131     * This is intended to be used if you want to anonymously send a party an encrypted message.
132     */
133    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen(js_name = encryptWithEphemeralKey))]
134    pub fn encrypt_with_ephemeral_private_key(message: &[u8], recipient_pub_key: &PublicKey) -> Result<ECIESCiphertext, JsValue> {
135        match ECIES::encrypt_with_ephemeral_private_key_impl(message, recipient_pub_key) {
136            Ok(v) => Ok(v),
137            Err(e) => Err(JsValue::from_str(&e.to_string())),
138        }
139    }
140
141    pub fn decrypt(ciphertext: &ECIESCiphertext, recipient_priv_key: &PrivateKey, sender_pub_key: &PublicKey) -> Result<Vec<u8>, JsValue> {
142        match ECIES::decrypt_impl(ciphertext, recipient_priv_key, sender_pub_key) {
143            Ok(v) => Ok(v),
144            Err(e) => Err(JsValue::from_str(&e.to_string())),
145        }
146    }
147
148    #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen(js_name = deriveCipherKeys))]
149    pub fn derive_cipher_keys(priv_key: &PrivateKey, pub_key: &PublicKey) -> Result<CipherKeys, JsValue> {
150        match ECIES::derive_cipher_keys_impl(priv_key, pub_key) {
151            Ok(v) => Ok(v),
152            Err(e) => Err(JsValue::from_str(&e.to_string())),
153        }
154    }
155}
156
157#[cfg(not(target_arch = "wasm32"))]
158impl ECIES {
159    pub fn encrypt(message: &[u8], sender_priv_key: &PrivateKey, recipient_pub_key: &PublicKey, exclude_pub_key: bool) -> Result<ECIESCiphertext, BSVErrors> {
160        ECIES::encrypt_impl(message, sender_priv_key, recipient_pub_key, exclude_pub_key)
161    }
162
163    /**
164     * Encrypt with a randomly generate private key.
165     * This is intended to be used if you want to anonymously send a party an encrypted message.
166     */
167    pub fn encrypt_with_ephemeral_private_key(message: &[u8], recipient_pub_key: &PublicKey) -> Result<ECIESCiphertext, BSVErrors> {
168        ECIES::encrypt_with_ephemeral_private_key_impl(message, recipient_pub_key)
169    }
170
171    pub fn decrypt(ciphertext: &ECIESCiphertext, recipient_priv_key: &PrivateKey, sender_pub_key: &PublicKey) -> Result<Vec<u8>, BSVErrors> {
172        ECIES::decrypt_impl(ciphertext, recipient_priv_key, sender_pub_key)
173    }
174
175    pub fn derive_cipher_keys(priv_key: &PrivateKey, pub_key: &PublicKey) -> Result<CipherKeys, BSVErrors> {
176        ECIES::derive_cipher_keys_impl(priv_key, pub_key)
177    }
178}